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 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
55 #import <WebCore/WebScriptObject.h>
56 //#include <WebCore/DOMHTML.h>
58 #include <WebKit/WebFrame.h>
59 #include <WebKit/WebPolicyDelegate.h>
60 #include <WebKit/WebView.h>
62 #import <WebKit/WebView-WebPrivate.h>
67 #include <ext/stdio_filebuf.h>
69 #include <apt-pkg/acquire.h>
70 #include <apt-pkg/acquire-item.h>
71 #include <apt-pkg/algorithms.h>
72 #include <apt-pkg/cachefile.h>
73 #include <apt-pkg/clean.h>
74 #include <apt-pkg/configuration.h>
75 #include <apt-pkg/debmetaindex.h>
76 #include <apt-pkg/error.h>
77 #include <apt-pkg/init.h>
78 #include <apt-pkg/mmap.h>
79 #include <apt-pkg/pkgrecords.h>
80 #include <apt-pkg/sha1.h>
81 #include <apt-pkg/sourcelist.h>
82 #include <apt-pkg/sptr.h>
83 #include <apt-pkg/strutl.h>
85 #include <sys/types.h>
87 #include <sys/sysctl.h>
93 #include <mach-o/nlist.h>
103 #import "BrowserView.h"
104 #import "ResetView.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 /* Objective-C Handle<> {{{ */
120 template <typename Type_>
122 typedef _H<Type_> This_;
127 _finline void Retain_() {
132 _finline void Clear_() {
138 _finline _H(Type_ *value = NULL, bool mended = false) :
149 _finline This_ &operator =(Type_ *value) {
150 if (value_ != value) {
159 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
161 void NSLogRect(const char *fix, const CGRect &rect) {
162 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
165 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
167 /* iPhoneOS 2.0 Compatibility {{{ */
169 @interface UITextView (iPhoneOS)
170 - (void) setTextSize:(float)size;
173 @implementation UITextView (iPhoneOS)
175 - (void) setTextSize:(float)size {
176 [self setFont:[[self font] fontWithSize:size]];
183 extern NSString * const kCAFilterNearest;
185 /* Information Dictionaries {{{ */
186 @interface NSMutableArray (Cydia)
187 - (void) addInfoDictionary:(NSDictionary *)info;
190 @implementation NSMutableArray (Cydia)
192 - (void) addInfoDictionary:(NSDictionary *)info {
193 [self addObject:info];
198 @interface NSMutableDictionary (Cydia)
199 - (void) addInfoDictionary:(NSDictionary *)info;
202 @implementation NSMutableDictionary (Cydia)
204 - (void) addInfoDictionary:(NSDictionary *)info {
205 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
206 [self setObject:info forKey:bundle];
211 /* Pop Transitions {{{ */
212 @interface PopTransitionView : UITransitionView {
217 @implementation PopTransitionView
219 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
220 if (from != nil && to == nil)
221 [self removeFromSuperview];
226 @interface UIView (PopUpView)
227 - (void) popFromSuperviewAnimated:(BOOL)animated;
228 - (void) popSubview:(UIView *)view;
231 @implementation UIView (PopUpView)
233 - (void) popFromSuperviewAnimated:(BOOL)animated {
234 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
237 - (void) popSubview:(UIView *)view {
238 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
239 [transition setDelegate:transition];
240 [self addSubview:transition];
242 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
243 [transition transition:UITransitionNone toView:blank];
244 [transition transition:UITransitionPushFromBottom toView:view];
250 #define lprintf(args...) fprintf(stderr, args)
253 #define ForSaurik 1 && !ForRelease
254 #define RecycleWebViews 0
255 #define AlwaysReload 0 && !ForRelease
258 @interface NSMutableArray (Radix)
259 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
262 @implementation NSMutableArray (Radix)
264 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
265 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
266 [invocation setSelector:selector];
267 [invocation setArgument:&object atIndex:2];
269 size_t count([self count]);
274 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
276 for (size_t i(0); i != count; ++i) {
277 RadixItem &item(lhs[i]);
280 id object([self objectAtIndex:i]);
281 [invocation setTarget:object];
284 [invocation getReturnValue:&item.key];
287 static const size_t width = 32;
288 static const size_t bits = 11;
289 static const size_t slots = 1 << bits;
290 static const size_t passes = (width + (bits - 1)) / bits;
292 size_t *hist(new size_t[slots]);
294 for (size_t pass(0); pass != passes; ++pass) {
295 memset(hist, 0, sizeof(size_t) * slots);
297 for (size_t i(0); i != count; ++i) {
298 uint32_t key(lhs[i].key);
300 key &= _not(uint32_t) >> width - bits;
305 for (size_t i(0); i != slots; ++i) {
306 size_t local(offset);
311 for (size_t i(0); i != count; ++i) {
312 uint32_t key(lhs[i].key);
314 key &= _not(uint32_t) >> width - bits;
315 rhs[hist[key]++] = lhs[i];
325 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
326 for (size_t i(0); i != count; ++i)
327 [values addObject:[self objectAtIndex:lhs[i].index]];
328 [self setArray:values];
337 kUIControlEventMouseDown = 1 << 0,
338 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
339 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
340 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
341 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
342 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
343 } UIControlEventMasks;
345 @interface NSString (UIKit)
346 - (NSString *) stringByAddingPercentEscapes;
347 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
350 @interface NSString (Cydia)
351 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
352 - (NSComparisonResult) compareByPath:(NSString *)other;
355 @implementation NSString (Cydia)
357 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
358 char data[length + 1];
359 memcpy(data, bytes, length);
361 return [NSString stringWithUTF8String:data];
364 - (NSComparisonResult) compareByPath:(NSString *)other {
365 NSString *prefix = [self commonPrefixWithString:other options:0];
366 size_t length = [prefix length];
368 NSRange lrange = NSMakeRange(length, [self length] - length);
369 NSRange rrange = NSMakeRange(length, [other length] - length);
371 lrange = [self rangeOfString:@"/" options:0 range:lrange];
372 rrange = [other rangeOfString:@"/" options:0 range:rrange];
374 NSComparisonResult value;
376 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
377 value = NSOrderedSame;
378 else if (lrange.location == NSNotFound)
379 value = NSOrderedAscending;
380 else if (rrange.location == NSNotFound)
381 value = NSOrderedDescending;
383 value = NSOrderedSame;
385 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
386 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
387 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
388 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
390 NSComparisonResult result = [lpath compare:rpath];
391 return result == NSOrderedSame ? value : result;
396 /* Perl-Compatible RegEx {{{ */
406 Pcre(const char *regex) :
411 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
414 lprintf("%d:%s\n", offset, error);
418 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
419 matches_ = new int[(capture_ + 1) * 3];
427 NSString *operator [](size_t match) {
428 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
431 bool operator ()(NSString *data) {
432 // XXX: length is for characters, not for bytes
433 return operator ()([data UTF8String], [data length]);
436 bool operator ()(const char *data, size_t size) {
438 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
442 /* Mime Addresses {{{ */
443 @interface Address : NSObject {
449 - (NSString *) address;
451 + (Address *) addressWithString:(NSString *)string;
452 - (Address *) initWithString:(NSString *)string;
455 @implementation Address
464 - (NSString *) name {
468 - (NSString *) address {
472 + (Address *) addressWithString:(NSString *)string {
473 return [[[Address alloc] initWithString:string] autorelease];
476 + (NSArray *) _attributeKeys {
477 return [NSArray arrayWithObjects:@"address", @"name", nil];
480 - (NSArray *) attributeKeys {
481 return [[self class] _attributeKeys];
484 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
485 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
488 - (Address *) initWithString:(NSString *)string {
489 if ((self = [super init]) != nil) {
490 const char *data = [string UTF8String];
491 size_t size = [string length];
493 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
495 if (address_r(data, size)) {
496 name_ = [address_r[1] retain];
497 address_ = [address_r[2] retain];
499 name_ = [string retain];
507 /* CoreGraphics Primitives {{{ */
518 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
521 Set(space, red, green, blue, alpha);
526 CGColorRelease(color_);
533 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
535 float color[] = {red, green, blue, alpha};
536 color_ = CGColorCreate(space, color);
539 operator CGColorRef() {
545 extern "C" void UISetColor(CGColorRef color);
547 /* Random Global Variables {{{ */
548 static const int PulseInterval_ = 50000;
549 static const int ButtonBarHeight_ = 48;
550 static const float KeyboardTime_ = 0.3f;
552 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
553 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
555 static CGColor Blue_;
556 static CGColor Blueish_;
557 static CGColor Black_;
559 static CGColor White_;
560 static CGColor Gray_;
562 static NSString *App_;
563 static NSString *Home_;
564 static BOOL Sounds_Keyboard_;
566 static BOOL Advanced_;
570 static BOOL Ignored_;
572 static UIFont *Font12_;
573 static UIFont *Font12Bold_;
574 static UIFont *Font14_;
575 static UIFont *Font18Bold_;
576 static UIFont *Font22Bold_;
578 static const char *Machine_ = NULL;
579 static const NSString *UniqueID_ = NULL;
586 CGColorSpaceRef space_;
588 #define FW_LEAST(major, minor, bugfix) \
589 (major < Major_ || major == Major_ && \
590 (minor < Minor_ || minor == Minor_ && \
596 static NSDictionary *SectionMap_;
597 static NSMutableDictionary *Metadata_;
598 static _transient NSMutableDictionary *Settings_;
599 static _transient NSString *Role_;
600 static _transient NSMutableDictionary *Packages_;
601 static _transient NSMutableDictionary *Sections_;
602 static _transient NSMutableDictionary *Sources_;
603 static bool Changed_;
607 static NSMutableArray *Documents_;
610 NSString *GetLastUpdate() {
611 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
614 return @"Never or Unknown";
616 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
617 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
619 CFRelease(formatter);
621 return [(NSString *) formatted autorelease];
624 /* Display Helpers {{{ */
625 inline float Interpolate(float begin, float end, float fraction) {
626 return (end - begin) * fraction + begin;
629 NSString *SizeString(double size) {
630 bool negative = size < 0;
635 while (size > 1024) {
640 static const char *powers_[] = {"B", "kB", "MB", "GB"};
642 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
645 NSString *StripVersion(NSString *version) {
646 NSRange colon = [version rangeOfString:@":"];
647 if (colon.location != NSNotFound)
648 version = [version substringFromIndex:(colon.location + 1)];
652 NSString *Simplify(NSString *title) {
653 const char *data = [title UTF8String];
654 size_t size = [title length];
656 static Pcre square_r("^\\[(.*)\\]$");
657 if (square_r(data, size))
658 return Simplify(square_r[1]);
660 static Pcre paren_r("^\\((.*)\\)$");
661 if (paren_r(data, size))
662 return Simplify(paren_r[1]);
664 static Pcre title_r("^(.*?) \\(.*\\)$");
665 if (title_r(data, size))
666 return Simplify(title_r[1]);
672 bool isSectionVisible(NSString *section) {
673 NSDictionary *metadata = [Sections_ objectForKey:section];
674 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
675 return hidden == nil || ![hidden boolValue];
678 /* Delegate Prototypes {{{ */
682 @interface NSObject (ProgressDelegate)
685 @implementation NSObject(ProgressDelegate)
687 - (void) _setProgressError:(NSArray *)args {
688 [self performSelector:@selector(setProgressError:forPackage:)
689 withObject:[args objectAtIndex:0]
690 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
696 @protocol ProgressDelegate
697 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
698 - (void) setProgressTitle:(NSString *)title;
699 - (void) setProgressPercent:(float)percent;
700 - (void) startProgress;
701 - (void) addProgressOutput:(NSString *)output;
702 - (bool) isCancelling:(size_t)received;
705 @protocol ConfigurationDelegate
706 - (void) repairWithSelector:(SEL)selector;
707 - (void) setConfigurationData:(NSString *)data;
710 @protocol CydiaDelegate
711 - (void) installPackage:(Package *)package;
712 - (void) removePackage:(Package *)package;
713 - (void) slideUp:(UIActionSheet *)alert;
714 - (void) distUpgrade;
717 - (void) askForSettings;
718 - (UIProgressHUD *) addProgressHUD;
719 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
720 - (RVPage *) pageForPackage:(NSString *)name;
721 - (void) openMailToURL:(NSURL *)url;
725 /* Status Delegation {{{ */
727 public pkgAcquireStatus
730 _transient NSObject<ProgressDelegate> *delegate_;
738 void setDelegate(id delegate) {
739 delegate_ = delegate;
742 virtual bool MediaChange(std::string media, std::string drive) {
746 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
749 virtual void Fetch(pkgAcquire::ItemDesc &item) {
750 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
751 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
754 virtual void Done(pkgAcquire::ItemDesc &item) {
757 virtual void Fail(pkgAcquire::ItemDesc &item) {
759 item.Owner->Status == pkgAcquire::Item::StatIdle ||
760 item.Owner->Status == pkgAcquire::Item::StatDone
764 std::string &error(item.Owner->ErrorText);
768 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
769 NSArray *fields([description componentsSeparatedByString:@" "]);
770 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
772 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
773 withObject:[NSArray arrayWithObjects:
774 [NSString stringWithUTF8String:error.c_str()],
781 virtual bool Pulse(pkgAcquire *Owner) {
782 bool value = pkgAcquireStatus::Pulse(Owner);
785 double(CurrentBytes + CurrentItems) /
786 double(TotalBytes + TotalItems)
789 [delegate_ setProgressPercent:percent];
790 return [delegate_ isCancelling:CurrentBytes] ? false : value;
793 virtual void Start() {
794 [delegate_ startProgress];
797 virtual void Stop() {
801 /* Progress Delegation {{{ */
806 _transient id<ProgressDelegate> delegate_;
809 virtual void Update() {
810 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
811 [delegate_ setProgressPercent:(Percent / 100)];
820 void setDelegate(id delegate) {
821 delegate_ = delegate;
824 virtual void Done() {
825 [delegate_ setProgressPercent:1];
830 /* Database Interface {{{ */
831 @interface Database : NSObject {
833 pkgDepCache::Policy *policy_;
834 pkgRecords *records_;
835 pkgProblemResolver *resolver_;
836 pkgAcquire *fetcher_;
838 SPtr<pkgPackageManager> manager_;
839 pkgSourceList *list_;
841 NSMutableDictionary *sources_;
842 NSMutableArray *packages_;
844 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
853 + (Database *) sharedInstance;
855 - (void) _readCydia:(NSNumber *)fd;
856 - (void) _readStatus:(NSNumber *)fd;
857 - (void) _readOutput:(NSNumber *)fd;
861 - (Package *) packageWithName:(NSString *)name;
863 - (pkgCacheFile &) cache;
864 - (pkgDepCache::Policy *) policy;
865 - (pkgRecords *) records;
866 - (pkgProblemResolver *) resolver;
867 - (pkgAcquire &) fetcher;
868 - (NSArray *) packages;
869 - (NSArray *) sources;
878 - (void) updateWithStatus:(Status &)status;
880 - (void) setDelegate:(id)delegate;
881 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
885 /* Source Class {{{ */
886 @interface Source : NSObject {
887 NSString *description_;
892 NSString *distribution_;
896 NSString *defaultIcon_;
898 NSDictionary *record_;
902 - (Source *) initWithMetaIndex:(metaIndex *)index;
904 - (NSComparisonResult) compareByNameAndType:(Source *)source;
906 - (NSDictionary *) record;
910 - (NSString *) distribution;
916 - (NSString *) description;
917 - (NSString *) label;
918 - (NSString *) origin;
919 - (NSString *) version;
921 - (NSString *) defaultIcon;
925 @implementation Source
929 [distribution_ release];
932 if (description_ != nil)
933 [description_ release];
940 if (defaultIcon_ != nil)
941 [defaultIcon_ release];
948 + (NSArray *) _attributeKeys {
949 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
952 - (NSArray *) attributeKeys {
953 return [[self class] _attributeKeys];
956 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
957 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
960 - (Source *) initWithMetaIndex:(metaIndex *)index {
961 if ((self = [super init]) != nil) {
962 trusted_ = index->IsTrusted();
964 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
965 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
966 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
968 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
969 if (dindex != NULL) {
970 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
972 while (std::getline(release, line)) {
973 std::string::size_type colon(line.find(':'));
974 if (colon == std::string::npos)
977 std::string name(line.substr(0, colon));
978 std::string value(line.substr(colon + 1));
979 while (!value.empty() && value[0] == ' ')
980 value = value.substr(1);
982 if (name == "Default-Icon")
983 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
984 else if (name == "Description")
985 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
986 else if (name == "Label")
987 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
988 else if (name == "Origin")
989 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
990 else if (name == "Version")
991 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
995 record_ = [Sources_ objectForKey:[self key]];
997 record_ = [record_ retain];
1001 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1002 NSDictionary *lhr = [self record];
1003 NSDictionary *rhr = [source record];
1006 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1008 NSString *lhs = [self name];
1009 NSString *rhs = [source name];
1011 if ([lhs length] != 0 && [rhs length] != 0) {
1012 unichar lhc = [lhs characterAtIndex:0];
1013 unichar rhc = [rhs characterAtIndex:0];
1015 if (isalpha(lhc) && !isalpha(rhc))
1016 return NSOrderedAscending;
1017 else if (!isalpha(lhc) && isalpha(rhc))
1018 return NSOrderedDescending;
1021 return [lhs compare:rhs options:CompareOptions_];
1024 - (NSDictionary *) record {
1032 - (NSString *) uri {
1036 - (NSString *) distribution {
1037 return distribution_;
1040 - (NSString *) type {
1044 - (NSString *) key {
1045 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1048 - (NSString *) host {
1049 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1052 - (NSString *) name {
1053 return origin_ == nil ? [self host] : origin_;
1056 - (NSString *) description {
1057 return description_;
1060 - (NSString *) label {
1061 return label_ == nil ? [self host] : label_;
1064 - (NSString *) origin {
1068 - (NSString *) version {
1072 - (NSString *) defaultIcon {
1073 return defaultIcon_;
1078 /* Relationship Class {{{ */
1079 @interface Relationship : NSObject {
1084 - (NSString *) type;
1086 - (NSString *) name;
1090 @implementation Relationship
1098 - (NSString *) type {
1106 - (NSString *) name {
1113 /* Package Class {{{ */
1114 NSString *Scour(const char *field, const char *begin, const char *end) {
1115 size_t i(0), l(strlen(field));
1118 const char *name = begin + i;
1119 const char *colon = name + l;
1120 const char *value = colon + 1;
1125 strncasecmp(name, field, l) == 0
1127 while (value != end && value[0] == ' ')
1129 const char *line = std::find(value, end, '\n');
1130 while (line != value && line[-1] == ' ')
1133 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1135 begin = std::find(begin, end, '\n');
1143 @interface Package : NSObject {
1144 pkgCache::PkgIterator iterator_;
1145 _transient Database *database_;
1146 pkgCache::VerIterator version_;
1147 pkgCache::VerFileIterator file_;
1155 NSString *installed_;
1161 NSString *depiction_;
1162 NSString *homepage_;
1168 NSArray *relationships_;
1171 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1172 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1174 - (pkgCache::PkgIterator) iterator;
1176 - (NSString *) section;
1177 - (NSString *) simpleSection;
1179 - (Address *) maintainer;
1181 - (NSString *) description;
1182 - (NSString *) index;
1184 - (NSMutableDictionary *) metadata;
1186 - (BOOL) subscribed;
1189 - (NSString *) latest;
1190 - (NSString *) installed;
1193 - (BOOL) upgradableAndEssential:(BOOL)essential;
1196 - (BOOL) unfiltered;
1200 - (BOOL) halfConfigured;
1201 - (BOOL) halfInstalled;
1203 - (NSString *) mode;
1206 - (NSString *) name;
1207 - (NSString *) tagline;
1209 - (NSString *) homepage;
1210 - (NSString *) depiction;
1211 - (Address *) author;
1213 - (NSArray *) files;
1214 - (NSArray *) relationships;
1215 - (NSArray *) warnings;
1216 - (NSArray *) applications;
1218 - (Source *) source;
1219 - (NSString *) role;
1221 - (BOOL) matches:(NSString *)text;
1223 - (bool) hasSupportingRole;
1224 - (BOOL) hasTag:(NSString *)tag;
1225 - (NSString *) primaryPurpose;
1226 - (NSArray *) purposes;
1228 - (NSComparisonResult) compareByName:(Package *)package;
1229 - (NSComparisonResult) compareBySection:(Package *)package;
1231 - (uint32_t) compareForChanges;
1236 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1237 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1238 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1239 - (NSNumber *) isVisibleInSource:(Source *)source;
1243 @implementation Package
1249 if (section_ != nil)
1253 if (installed_ != nil)
1254 [installed_ release];
1262 if (depiction_ != nil)
1263 [depiction_ release];
1264 if (homepage_ != nil)
1265 [homepage_ release];
1266 if (sponsor_ != nil)
1275 if (relationships_ != nil)
1276 [relationships_ release];
1281 + (NSArray *) _attributeKeys {
1282 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1285 - (NSArray *) attributeKeys {
1286 return [[self class] _attributeKeys];
1289 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1290 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1293 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1294 if ((self = [super init]) != nil) {
1295 iterator_ = iterator;
1296 database_ = database;
1298 version_ = [database_ policy]->GetCandidateVer(iterator_);
1299 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1300 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1302 pkgCache::VerIterator current = iterator_.CurrentVer();
1303 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1304 installed_ = [StripVersion(installed) retain];
1306 if (!version_.end())
1307 file_ = version_.FileList();
1309 pkgCache &cache([database_ cache]);
1310 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1313 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1316 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1318 const char *begin, *end;
1319 parser->GetRec(begin, end);
1321 name_ = Scour("name", begin, end);
1323 name_ = [name_ retain];
1324 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1325 icon_ = Scour("icon", begin, end);
1327 icon_ = [icon_ retain];
1328 depiction_ = Scour("depiction", begin, end);
1329 if (depiction_ != nil)
1330 depiction_ = [depiction_ retain];
1331 homepage_ = Scour("homepage", begin, end);
1332 if (homepage_ == nil)
1333 homepage_ = Scour("website", begin, end);
1334 if ([homepage_ isEqualToString:depiction_])
1336 if (homepage_ != nil)
1337 homepage_ = [homepage_ retain];
1338 NSString *sponsor = Scour("sponsor", begin, end);
1340 sponsor_ = [[Address addressWithString:sponsor] retain];
1341 NSString *author = Scour("author", begin, end);
1343 author_ = [[Address addressWithString:author] retain];
1344 NSString *tags = Scour("tag", begin, end);
1346 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1350 for (int i(0), e([tags_ count]); i != e; ++i) {
1351 NSString *tag = [tags_ objectAtIndex:i];
1352 if ([tag hasPrefix:@"role::"]) {
1353 role_ = [[tag substringFromIndex:6] retain];
1358 NSString *solid(latest == nil ? installed : latest);
1359 bool changed(false);
1361 NSString *key([id_ lowercaseString]);
1363 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1364 if (metadata == nil) {
1365 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1370 [metadata setObject:solid forKey:@"LastVersion"];
1373 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1374 NSDate *last([metadata objectForKey:@"LastSeen"]);
1375 NSString *version([metadata objectForKey:@"LastVersion"]);
1378 first = last == nil ? now_ : last;
1379 [metadata setObject:first forKey:@"FirstSeen"];
1384 if (version == nil) {
1385 [metadata setObject:solid forKey:@"LastVersion"];
1387 } else if (![version isEqualToString:solid]) {
1388 [metadata setObject:solid forKey:@"LastVersion"];
1390 [metadata setObject:last forKey:@"LastSeen"];
1396 [Packages_ setObject:metadata forKey:key];
1402 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1403 return [[[Package alloc]
1404 initWithIterator:iterator
1409 - (pkgCache::PkgIterator) iterator {
1413 - (NSString *) section {
1414 if (section_ != nil)
1417 const char *section = iterator_.Section();
1418 if (section == NULL)
1421 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1424 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1425 if (NSString *rename = [value objectForKey:@"Rename"]) {
1430 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1434 - (NSString *) simpleSection {
1435 if (NSString *section = [self section])
1436 return Simplify(section);
1442 - (Address *) maintainer {
1445 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1446 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1450 return version_.end() ? 0 : version_->InstalledSize;
1453 - (NSString *) description {
1456 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1457 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1459 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1460 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1461 if ([lines count] < 2)
1464 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1465 for (size_t i(1); i != [lines count]; ++i) {
1466 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1467 [trimmed addObject:trim];
1470 return [trimmed componentsJoinedByString:@"\n"];
1473 - (NSString *) index {
1474 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1475 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1478 - (NSMutableDictionary *) metadata {
1479 return [Packages_ objectForKey:[id_ lowercaseString]];
1483 NSDictionary *metadata([self metadata]);
1484 if ([self subscribed])
1485 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1487 return [metadata objectForKey:@"FirstSeen"];
1490 - (BOOL) subscribed {
1491 NSDictionary *metadata([self metadata]);
1492 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1493 return [subscribed boolValue];
1499 NSDictionary *metadata([self metadata]);
1500 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1501 return [ignored boolValue];
1506 - (NSString *) latest {
1510 - (NSString *) installed {
1515 return !version_.end();
1518 - (BOOL) upgradableAndEssential:(BOOL)essential {
1519 pkgCache::VerIterator current = iterator_.CurrentVer();
1522 return essential && [self essential];
1524 return !version_.end() && version_ != current;
1527 - (BOOL) essential {
1528 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1532 return [database_ cache][iterator_].InstBroken();
1535 - (BOOL) unfiltered {
1536 NSString *section = [self section];
1537 return section == nil || isSectionVisible(section);
1541 return [self hasSupportingRole] && [self unfiltered];
1545 unsigned char current = iterator_->CurrentState;
1546 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1549 - (BOOL) halfConfigured {
1550 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1553 - (BOOL) halfInstalled {
1554 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1558 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1559 return state.Mode != pkgDepCache::ModeKeep;
1562 - (NSString *) mode {
1563 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1565 switch (state.Mode) {
1566 case pkgDepCache::ModeDelete:
1567 if ((state.iFlags & pkgDepCache::Purge) != 0)
1571 case pkgDepCache::ModeKeep:
1572 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1576 case pkgDepCache::ModeInstall:
1577 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1578 return @"Reinstall";
1579 else switch (state.Status) {
1581 return @"Downgrade";
1587 return @"New Install";
1600 - (NSString *) name {
1601 return name_ == nil ? id_ : name_;
1604 - (NSString *) tagline {
1608 - (UIImage *) icon {
1609 NSString *section = [self simpleSection];
1612 if (NSString *icon = icon_)
1613 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1614 if (icon == nil) if (section != nil)
1615 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1616 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1617 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1619 icon = [UIImage applicationImageNamed:@"unknown.png"];
1623 - (NSString *) homepage {
1627 - (NSString *) depiction {
1631 - (Address *) sponsor {
1635 - (Address *) author {
1639 - (NSArray *) files {
1640 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1641 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1644 fin.open([path UTF8String]);
1649 while (std::getline(fin, line))
1650 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1655 - (NSArray *) relationships {
1656 return relationships_;
1659 - (NSArray *) warnings {
1660 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1661 const char *name(iterator_.Name());
1663 size_t length(strlen(name));
1664 if (length < 2) invalid:
1665 [warnings addObject:@"illegal package identifier"];
1666 else for (size_t i(0); i != length; ++i)
1668 (name[i] < 'a' || name[i] > 'z') &&
1669 (name[i] < '0' || name[i] > '9') &&
1670 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1673 if (strcmp(name, "cydia") != 0) {
1677 if (NSArray *files = [self files])
1678 for (NSString *file in files)
1679 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1681 else if (!stash && [file isEqualToString:@"/var/stash"])
1685 [warnings addObject:@"files installed into Cydia.app"];
1687 [warnings addObject:@"files installed to /var/stash"];
1690 return [warnings count] == 0 ? nil : warnings;
1693 - (NSArray *) applications {
1694 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1696 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1698 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1699 if (NSArray *files = [self files])
1700 for (NSString *file in files)
1701 if (application_r(file)) {
1702 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1703 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1704 if ([id isEqualToString:me])
1707 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1709 display = application_r[1];
1711 NSString *bundle([file stringByDeletingLastPathComponent]);
1712 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1713 if (icon == nil || [icon length] == 0)
1715 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1717 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1718 [applications addObject:application];
1720 [application addObject:id];
1721 [application addObject:display];
1722 [application addObject:url];
1725 return [applications count] == 0 ? nil : applications;
1728 - (Source *) source {
1730 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1737 - (NSString *) role {
1741 - (BOOL) matches:(NSString *)text {
1747 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1748 if (range.location != NSNotFound)
1751 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1752 if (range.location != NSNotFound)
1755 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1756 if (range.location != NSNotFound)
1762 - (bool) hasSupportingRole {
1765 if ([role_ isEqualToString:@"enduser"])
1767 if ([Role_ isEqualToString:@"User"])
1769 if ([role_ isEqualToString:@"hacker"])
1771 if ([Role_ isEqualToString:@"Hacker"])
1773 if ([role_ isEqualToString:@"developer"])
1775 if ([Role_ isEqualToString:@"Developer"])
1780 - (BOOL) hasTag:(NSString *)tag {
1781 return tags_ == nil ? NO : [tags_ containsObject:tag];
1784 - (NSString *) primaryPurpose {
1785 for (NSString *tag in tags_)
1786 if ([tag hasPrefix:@"purpose::"])
1787 return [tag substringFromIndex:9];
1791 - (NSArray *) purposes {
1792 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1793 for (NSString *tag in tags_)
1794 if ([tag hasPrefix:@"purpose::"])
1795 [purposes addObject:[tag substringFromIndex:9]];
1796 return [purposes count] == 0 ? nil : purposes;
1799 - (NSComparisonResult) compareByName:(Package *)package {
1800 NSString *lhs = [self name];
1801 NSString *rhs = [package name];
1803 if ([lhs length] != 0 && [rhs length] != 0) {
1804 unichar lhc = [lhs characterAtIndex:0];
1805 unichar rhc = [rhs characterAtIndex:0];
1807 if (isalpha(lhc) && !isalpha(rhc))
1808 return NSOrderedAscending;
1809 else if (!isalpha(lhc) && isalpha(rhc))
1810 return NSOrderedDescending;
1813 return [lhs compare:rhs options:CompareOptions_];
1816 - (NSComparisonResult) compareBySection:(Package *)package {
1817 NSString *lhs = [self section];
1818 NSString *rhs = [package section];
1820 if (lhs == NULL && rhs != NULL)
1821 return NSOrderedAscending;
1822 else if (lhs != NULL && rhs == NULL)
1823 return NSOrderedDescending;
1824 else if (lhs != NULL && rhs != NULL) {
1825 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1826 if (result != NSOrderedSame)
1830 return NSOrderedSame;
1833 - (uint32_t) compareForChanges {
1838 uint32_t timestamp : 30;
1839 uint32_t ignored : 1;
1840 uint32_t upgradable : 1;
1844 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1846 if ([self upgradableAndEssential:YES]) {
1847 value.bits.timestamp = 0;
1848 value.bits.ignored = [self ignored] ? 0 : 1;
1849 value.bits.upgradable = 1;
1851 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1852 value.bits.ignored = 0;
1853 value.bits.upgradable = 0;
1856 return _not(uint32_t) - value.key;
1860 pkgProblemResolver *resolver = [database_ resolver];
1861 resolver->Clear(iterator_);
1862 resolver->Protect(iterator_);
1863 pkgCacheFile &cache([database_ cache]);
1864 cache->MarkInstall(iterator_, false);
1865 pkgDepCache::StateCache &state((*cache)[iterator_]);
1866 if (!state.Install())
1867 cache->SetReInstall(iterator_, true);
1871 pkgProblemResolver *resolver = [database_ resolver];
1872 resolver->Clear(iterator_);
1873 resolver->Protect(iterator_);
1874 resolver->Remove(iterator_);
1875 [database_ cache]->MarkDelete(iterator_, true);
1878 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1879 return [NSNumber numberWithBool:(
1880 [self unfiltered] && [self matches:search]
1884 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1885 return [NSNumber numberWithBool:(
1886 (![number boolValue] || [self visible]) && [self installed] != nil
1890 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1891 NSString *section = [self section];
1893 return [NSNumber numberWithBool:(
1895 [self installed] == nil && (
1897 section == nil && [name length] == 0 ||
1898 [name isEqualToString:section]
1903 - (NSNumber *) isVisibleInSource:(Source *)source {
1904 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1909 /* Section Class {{{ */
1910 @interface Section : NSObject {
1916 - (NSComparisonResult) compareByName:(Section *)section;
1917 - (Section *) initWithName:(NSString *)name;
1918 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1919 - (NSString *) name;
1922 - (void) addToCount;
1926 @implementation Section
1933 - (NSComparisonResult) compareByName:(Section *)section {
1934 NSString *lhs = [self name];
1935 NSString *rhs = [section name];
1937 if ([lhs length] != 0 && [rhs length] != 0) {
1938 unichar lhc = [lhs characterAtIndex:0];
1939 unichar rhc = [rhs characterAtIndex:0];
1941 if (isalpha(lhc) && !isalpha(rhc))
1942 return NSOrderedAscending;
1943 else if (!isalpha(lhc) && isalpha(rhc))
1944 return NSOrderedDescending;
1947 return [lhs compare:rhs options:CompareOptions_];
1950 - (Section *) initWithName:(NSString *)name {
1951 return [self initWithName:name row:0];
1954 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1955 if ((self = [super init]) != nil) {
1956 name_ = [name retain];
1961 - (NSString *) name {
1973 - (void) addToCount {
1981 static NSArray *Finishes_;
1983 /* Database Implementation {{{ */
1984 @implementation Database
1986 + (Database *) sharedInstance {
1987 static Database *instance;
1988 if (instance == nil)
1989 instance = [[Database alloc] init];
1998 - (void) _readCydia:(NSNumber *)fd { _pooled
1999 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2000 std::istream is(&ib);
2003 static Pcre finish_r("^finish:([^:]*)$");
2005 while (std::getline(is, line)) {
2006 const char *data(line.c_str());
2007 size_t size = line.size();
2008 lprintf("C:%s\n", data);
2010 if (finish_r(data, size)) {
2011 NSString *finish = finish_r[1];
2012 int index = [Finishes_ indexOfObject:finish];
2013 if (index != INT_MAX && index > Finish_)
2021 - (void) _readStatus:(NSNumber *)fd { _pooled
2022 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2023 std::istream is(&ib);
2026 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2027 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2029 while (std::getline(is, line)) {
2030 const char *data(line.c_str());
2031 size_t size = line.size();
2032 lprintf("S:%s\n", data);
2034 if (conffile_r(data, size)) {
2035 [delegate_ setConfigurationData:conffile_r[1]];
2036 } else if (strncmp(data, "status: ", 8) == 0) {
2037 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2038 [delegate_ setProgressTitle:string];
2039 } else if (pmstatus_r(data, size)) {
2040 std::string type([pmstatus_r[1] UTF8String]);
2041 NSString *id = pmstatus_r[2];
2043 float percent([pmstatus_r[3] floatValue]);
2044 [delegate_ setProgressPercent:(percent / 100)];
2046 NSString *string = pmstatus_r[4];
2048 if (type == "pmerror")
2049 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2050 withObject:[NSArray arrayWithObjects:string, id, nil]
2053 else if (type == "pmstatus") {
2054 [delegate_ setProgressTitle:string];
2055 } else if (type == "pmconffile")
2056 [delegate_ setConfigurationData:string];
2057 else _assert(false);
2058 } else _assert(false);
2064 - (void) _readOutput:(NSNumber *)fd { _pooled
2065 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2066 std::istream is(&ib);
2069 while (std::getline(is, line)) {
2070 lprintf("O:%s\n", line.c_str());
2071 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2081 - (Package *) packageWithName:(NSString *)name {
2082 if (static_cast<pkgDepCache *>(cache_) == NULL)
2084 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2085 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2088 - (Database *) init {
2089 if ((self = [super init]) != nil) {
2096 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2097 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2101 _assert(pipe(fds) != -1);
2104 _config->Set("APT::Keep-Fds::", cydiafd_);
2105 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2108 detachNewThreadSelector:@selector(_readCydia:)
2110 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2113 _assert(pipe(fds) != -1);
2117 detachNewThreadSelector:@selector(_readStatus:)
2119 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2122 _assert(pipe(fds) != -1);
2123 _assert(dup2(fds[0], 0) != -1);
2124 _assert(close(fds[0]) != -1);
2126 input_ = fdopen(fds[1], "a");
2128 _assert(pipe(fds) != -1);
2129 _assert(dup2(fds[1], 1) != -1);
2130 _assert(close(fds[1]) != -1);
2133 detachNewThreadSelector:@selector(_readOutput:)
2135 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2140 - (pkgCacheFile &) cache {
2144 - (pkgDepCache::Policy *) policy {
2148 - (pkgRecords *) records {
2152 - (pkgProblemResolver *) resolver {
2156 - (pkgAcquire &) fetcher {
2160 - (NSArray *) packages {
2164 - (NSArray *) sources {
2165 return [sources_ allValues];
2168 - (NSArray *) issues {
2169 if (cache_->BrokenCount() == 0)
2172 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2174 for (Package *package in packages_) {
2175 if (![package broken])
2177 pkgCache::PkgIterator pkg([package iterator]);
2179 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2180 [entry addObject:[package name]];
2181 [issues addObject:entry];
2183 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2187 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2188 pkgCache::DepIterator start;
2189 pkgCache::DepIterator end;
2190 dep.GlobOr(start, end); // ++dep
2192 if (!cache_->IsImportantDep(end))
2194 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2197 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2198 [entry addObject:failure];
2199 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2201 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2202 [failure addObject:[package name]];
2204 pkgCache::PkgIterator target(start.TargetPkg());
2205 if (target->ProvidesList != 0)
2206 [failure addObject:@"?"];
2208 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2210 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2211 else if (!cache_[target].CandidateVerIter(cache_).end())
2212 [failure addObject:@"-"];
2213 else if (target->ProvidesList == 0)
2214 [failure addObject:@"!"];
2216 [failure addObject:@"%"];
2220 if (start.TargetVer() != 0)
2221 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2232 - (void) reloadData {
2252 if (!cache_.Open(progress_, true)) {
2254 if (!_error->PopMessage(error))
2257 lprintf("cache_.Open():[%s]\n", error.c_str());
2259 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2260 [delegate_ repairWithSelector:@selector(configure)];
2261 else if (error == "The package lists or status file could not be parsed or opened.")
2262 [delegate_ repairWithSelector:@selector(update)];
2263 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2264 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2265 // else if (error == "The list of sources could not be read.")
2266 else _assert(false);
2272 now_ = [[NSDate date] retain];
2274 policy_ = new pkgDepCache::Policy();
2275 records_ = new pkgRecords(cache_);
2276 resolver_ = new pkgProblemResolver(cache_);
2277 fetcher_ = new pkgAcquire(&status_);
2280 list_ = new pkgSourceList();
2281 _assert(list_->ReadMainList());
2283 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2284 _assert(pkgApplyStatus(cache_));
2286 if (cache_->BrokenCount() != 0) {
2287 _assert(pkgFixBroken(cache_));
2288 _assert(cache_->BrokenCount() == 0);
2289 _assert(pkgMinimizeUpgrade(cache_));
2292 [sources_ removeAllObjects];
2293 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2294 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2295 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2297 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2298 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2302 [packages_ removeAllObjects];
2304 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2305 if (Package *package = [Package packageWithIterator:iterator database:self])
2306 [packages_ addObject:package];
2308 [packages_ sortUsingSelector:@selector(compareByName:)];
2312 - (void) configure {
2313 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2314 system([dpkg UTF8String]);
2322 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2323 _assert(!_error->PendingError());
2326 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2329 public pkgArchiveCleaner
2332 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2337 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2339 while (_error->PopMessage(error))
2340 lprintf("ArchiveCleaner: %s\n", error.c_str());
2345 pkgRecords records(cache_);
2347 lock_ = new FileFd();
2348 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2349 _assert(!_error->PendingError());
2352 // XXX: explain this with an error message
2353 _assert(list.ReadMainList());
2355 manager_ = (_system->CreatePM(cache_));
2356 _assert(manager_->GetArchives(fetcher_, &list, &records));
2357 _assert(!_error->PendingError());
2361 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2363 _assert(list.ReadMainList());
2364 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2365 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2368 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2373 bool failed = false;
2374 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2375 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2378 std::string uri = (*item)->DescURI();
2379 std::string error = (*item)->ErrorText;
2381 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2384 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2385 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2396 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2398 if (_error->PendingError()) {
2403 if (result == pkgPackageManager::Failed) {
2408 if (result != pkgPackageManager::Completed) {
2413 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2415 _assert(list.ReadMainList());
2416 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2417 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2420 if (![before isEqualToArray:after])
2425 _assert(pkgDistUpgrade(cache_));
2429 [self updateWithStatus:status_];
2432 - (void) updateWithStatus:(Status &)status {
2434 _assert(list.ReadMainList());
2437 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2438 _assert(!_error->PendingError());
2440 pkgAcquire fetcher(&status);
2441 _assert(list.GetIndexes(&fetcher));
2443 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2444 bool failed = false;
2445 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2446 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2447 (*item)->Finished();
2451 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2452 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2453 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2456 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2461 - (void) setDelegate:(id)delegate {
2462 delegate_ = delegate;
2463 status_.setDelegate(delegate);
2464 progress_.setDelegate(delegate);
2467 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2468 pkgIndexFile *index(NULL);
2469 list_->FindIndex(file, index);
2470 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2476 /* PopUp Windows {{{ */
2477 @interface PopUpView : UIView {
2478 _transient id delegate_;
2479 UITransitionView *transition_;
2484 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2488 @implementation PopUpView
2491 [transition_ setDelegate:nil];
2492 [transition_ release];
2498 [transition_ transition:UITransitionPushFromTop toView:nil];
2501 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2502 if (from != nil && to == nil)
2503 [self removeFromSuperview];
2506 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2507 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2508 delegate_ = delegate;
2510 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2511 [self addSubview:transition_];
2513 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2515 [view addSubview:self];
2517 [transition_ setDelegate:self];
2519 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2520 [transition_ transition:UITransitionNone toView:blank];
2521 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2528 /* Mail Composition {{{ */
2529 @interface MailToView : PopUpView {
2530 MailComposeController *controller_;
2533 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2537 @implementation MailToView
2540 [controller_ release];
2544 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2548 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2549 NSLog(@"did:%@", delivery);
2550 // [UIApp setStatusBarShowsProgress:NO];
2551 if ([controller error]){
2552 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2553 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2554 [mailAlertSheet setBodyText:[controller error]];
2555 [mailAlertSheet popupAlertAnimated:YES];
2559 - (void) showError {
2560 NSLog(@"%@", [controller_ error]);
2561 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2562 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2563 [mailAlertSheet setBodyText:[controller_ error]];
2564 [mailAlertSheet popupAlertAnimated:YES];
2567 - (void) deliverMessage { _pooled
2571 if (![controller_ deliverMessage])
2572 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2575 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2576 if ([controller_ needsDelivery])
2577 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2582 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2583 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2584 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2585 [controller_ setDelegate:self];
2586 [controller_ initializeUI];
2587 [controller_ setupForURL:url];
2589 UIView *view([controller_ view]);
2590 [overlay_ addSubview:view];
2596 /* Confirmation View {{{ */
2597 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2598 if (!iterator.end())
2599 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2600 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2602 pkgCache::PkgIterator package(dep.TargetPkg());
2605 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2612 @protocol ConfirmationViewDelegate
2617 @interface ConfirmationView : BrowserView {
2618 _transient Database *database_;
2619 UIActionSheet *essential_;
2626 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2630 @implementation ConfirmationView
2637 if (essential_ != nil)
2638 [essential_ release];
2644 [book_ popFromSuperviewAnimated:YES];
2647 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2648 NSString *context = [sheet context];
2650 if ([context isEqualToString:@"remove"])
2658 [delegate_ confirm];
2663 else if ([context isEqualToString:@"unable"])
2669 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2670 [window setValue:changes_ forKey:@"changes"];
2671 [window setValue:issues_ forKey:@"issues"];
2672 [window setValue:sizes_ forKey:@"sizes"];
2673 [super webView:sender didClearWindowObject:window forFrame:frame];
2676 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2677 if ((self = [super initWithBook:book]) != nil) {
2678 database_ = database;
2680 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2681 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2682 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2683 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2684 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2688 pkgDepCache::Policy *policy([database_ policy]);
2690 pkgCacheFile &cache([database_ cache]);
2691 NSArray *packages = [database_ packages];
2692 for (size_t i(0), e = [packages count]; i != e; ++i) {
2693 Package *package = [packages objectAtIndex:i];
2694 pkgCache::PkgIterator iterator = [package iterator];
2695 pkgDepCache::StateCache &state(cache[iterator]);
2697 NSString *name([package name]);
2699 if (state.NewInstall())
2700 [installing addObject:name];
2701 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2702 [reinstalling addObject:name];
2703 else if (state.Upgrade())
2704 [upgrading addObject:name];
2705 else if (state.Downgrade())
2706 [downgrading addObject:name];
2707 else if (state.Delete()) {
2708 if ([package essential])
2710 [removing addObject:name];
2713 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2714 substrate_ |= DepSubstrate(iterator.CurrentVer());
2719 else if (Advanced_ || true) {
2720 essential_ = [[UIActionSheet alloc]
2721 initWithTitle:@"Removing Essentials"
2722 buttons:[NSArray arrayWithObjects:
2723 @"Cancel Operation (Safe)",
2724 @"Force Removal (Unsafe)",
2726 defaultButtonIndex:0
2732 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2734 [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."];
2736 essential_ = [[UIActionSheet alloc]
2737 initWithTitle:@"Unable to Comply"
2738 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2739 defaultButtonIndex:0
2744 [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."];
2747 changes_ = [[NSArray alloc] initWithObjects:
2755 issues_ = [database_ issues];
2757 issues_ = [issues_ retain];
2759 sizes_ = [[NSArray alloc] initWithObjects:
2760 SizeString([database_ fetcher].FetchNeeded()),
2761 SizeString([database_ fetcher].PartialPresent()),
2762 SizeString([database_ cache]->UsrSize()),
2765 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2769 - (NSString *) backButtonTitle {
2773 - (NSString *) leftButtonTitle {
2777 - (NSString *) _rightButtonTitle {
2778 return issues_ == nil ? @"Confirm" : nil;
2781 - (void) _leftButtonClicked {
2786 - (void) _rightButtonClicked {
2787 if (essential_ != nil)
2788 [essential_ popupAlertAnimated:YES];
2792 [delegate_ confirm];
2800 /* Progress Data {{{ */
2801 @interface ProgressData : NSObject {
2807 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2814 @implementation ProgressData
2816 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2817 if ((self = [super init]) != nil) {
2818 selector_ = selector;
2838 /* Progress View {{{ */
2839 @interface ProgressView : UIView <
2840 ConfigurationDelegate,
2843 _transient Database *database_;
2845 UIView *background_;
2846 UITransitionView *transition_;
2848 UINavigationBar *navbar_;
2849 UIProgressBar *progress_;
2850 UITextView *output_;
2851 UITextLabel *status_;
2852 UIPushButton *close_;
2855 SHA1SumValue springlist_;
2856 SHA1SumValue sandplate_;
2858 NSTimeInterval last_;
2861 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2863 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2864 - (void) setContentView:(UIView *)view;
2867 - (void) _retachThread;
2868 - (void) _detachNewThreadData:(ProgressData *)data;
2869 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2875 @protocol ProgressViewDelegate
2876 - (void) progressViewIsComplete:(ProgressView *)sender;
2879 @implementation ProgressView
2882 [transition_ setDelegate:nil];
2883 [navbar_ setDelegate:nil];
2886 if (background_ != nil)
2887 [background_ release];
2888 [transition_ release];
2891 [progress_ release];
2898 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2899 if (bootstrap_ && from == overlay_ && to == view_)
2903 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2904 if ((self = [super initWithFrame:frame]) != nil) {
2905 database_ = database;
2906 delegate_ = delegate;
2908 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2909 [transition_ setDelegate:self];
2911 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2914 [overlay_ setBackgroundColor:[UIColor blackColor]];
2916 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2917 [background_ setBackgroundColor:[UIColor blackColor]];
2918 [self addSubview:background_];
2921 [self addSubview:transition_];
2923 CGSize navsize = [UINavigationBar defaultSize];
2924 CGRect navrect = {{0, 0}, navsize};
2926 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2927 [overlay_ addSubview:navbar_];
2929 [navbar_ setBarStyle:1];
2930 [navbar_ setDelegate:self];
2932 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2933 [navbar_ pushNavigationItem:navitem];
2935 CGRect bounds = [overlay_ bounds];
2936 CGSize prgsize = [UIProgressBar defaultSize];
2939 (bounds.size.width - prgsize.width) / 2,
2940 bounds.size.height - prgsize.height - 20
2943 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2944 [progress_ setStyle:0];
2946 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2948 bounds.size.height - prgsize.height - 50,
2949 bounds.size.width - 20,
2953 [status_ setColor:[UIColor whiteColor]];
2954 [status_ setBackgroundColor:[UIColor clearColor]];
2956 [status_ setCentersHorizontally:YES];
2957 //[status_ setFont:font];
2959 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2961 navrect.size.height + 20,
2962 bounds.size.width - 20,
2963 bounds.size.height - navsize.height - 62 - navrect.size.height
2966 //[output_ setTextFont:@"Courier New"];
2967 [output_ setTextSize:12];
2969 [output_ setTextColor:[UIColor whiteColor]];
2970 [output_ setBackgroundColor:[UIColor clearColor]];
2972 [output_ setMarginTop:0];
2973 [output_ setAllowsRubberBanding:YES];
2974 [output_ setEditable:NO];
2976 [overlay_ addSubview:output_];
2978 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2980 bounds.size.height - prgsize.height - 50,
2981 bounds.size.width - 20,
2985 [close_ setAutosizesToFit:NO];
2986 [close_ setDrawsShadow:YES];
2987 [close_ setStretchBackground:YES];
2988 [close_ setEnabled:YES];
2990 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2991 [close_ setTitleFont:bold];
2993 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2994 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2995 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2999 - (void) setContentView:(UIView *)view {
3000 view_ = [view retain];
3003 - (void) resetView {
3004 [transition_ transition:6 toView:view_];
3007 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3008 NSString *context = [sheet context];
3009 if ([context isEqualToString:@"conffile"]) {
3010 FILE *input = [database_ input];
3014 fprintf(input, "N\n");
3018 fprintf(input, "Y\n");
3029 - (void) closeButtonPushed {
3038 [delegate_ suspendWithAnimation:YES];
3042 system("launchctl stop com.apple.SpringBoard");
3046 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3055 - (void) _retachThread {
3056 UINavigationItem *item = [navbar_ topItem];
3057 [item setTitle:@"Complete"];
3059 [overlay_ addSubview:close_];
3060 [progress_ removeFromSuperview];
3061 [status_ removeFromSuperview];
3063 [delegate_ progressViewIsComplete:self];
3066 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3067 MMap mmap(file, MMap::ReadOnly);
3069 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3070 if (!(sandplate_ == sha1.Result()))
3075 FileFd file(SpringBoard_, FileFd::ReadOnly);
3076 MMap mmap(file, MMap::ReadOnly);
3078 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3079 if (!(springlist_ == sha1.Result()))
3084 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3085 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3086 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3087 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3088 case 4: [close_ setTitle:@"Reboot Device"]; break;
3091 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3093 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3094 [cache autorelease];
3096 NSFileManager *manager = [NSFileManager defaultManager];
3097 NSError *error = nil;
3099 id system = [cache objectForKey:@"System"];
3104 if (stat(Cache_, &info) == -1)
3107 [system removeAllObjects];
3109 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3110 for (NSString *app in apps)
3111 if ([app hasSuffix:@".app"]) {
3112 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3113 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3114 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3116 [info setObject:path forKey:@"Path"];
3117 [info setObject:@"System" forKey:@"ApplicationType"];
3118 [system addInfoDictionary:info];
3123 [cache writeToFile:@Cache_ atomically:YES];
3125 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3127 if (chmod(Cache_, info.st_mode) == -1)
3131 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3134 notify_post("com.apple.mobile.application_installed");
3136 [delegate_ setStatusBarShowsProgress:NO];
3139 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3140 [[data target] performSelector:[data selector] withObject:[data object]];
3143 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3146 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3147 UINavigationItem *item = [navbar_ topItem];
3148 [item setTitle:title];
3150 [status_ setText:nil];
3151 [output_ setText:@""];
3152 [progress_ setProgress:0];
3155 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3157 [close_ removeFromSuperview];
3158 [overlay_ addSubview:progress_];
3159 [overlay_ addSubview:status_];
3161 [delegate_ setStatusBarShowsProgress:YES];
3165 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3166 MMap mmap(file, MMap::ReadOnly);
3168 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3169 sandplate_ = sha1.Result();
3173 FileFd file(SpringBoard_, FileFd::ReadOnly);
3174 MMap mmap(file, MMap::ReadOnly);
3176 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3177 springlist_ = sha1.Result();
3180 [transition_ transition:6 toView:overlay_];
3183 detachNewThreadSelector:@selector(_detachNewThreadData:)
3185 withObject:[[ProgressData alloc]
3186 initWithSelector:selector
3193 - (void) repairWithSelector:(SEL)selector {
3195 detachNewThreadSelector:selector
3202 - (void) setConfigurationData:(NSString *)data {
3204 performSelectorOnMainThread:@selector(_setConfigurationData:)
3210 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3211 Package *package = id == nil ? nil : [database_ packageWithName:id];
3213 UIActionSheet *sheet = [[[UIActionSheet alloc]
3214 initWithTitle:(package == nil ? id : [package name])
3215 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3216 defaultButtonIndex:0
3221 [sheet setBodyText:error];
3222 [sheet popupAlertAnimated:YES];
3225 - (void) setProgressTitle:(NSString *)title {
3227 performSelectorOnMainThread:@selector(_setProgressTitle:)
3233 - (void) setProgressPercent:(float)percent {
3235 performSelectorOnMainThread:@selector(_setProgressPercent:)
3236 withObject:[NSNumber numberWithFloat:percent]
3241 - (void) startProgress {
3242 last_ = [NSDate timeIntervalSinceReferenceDate];
3245 - (void) addProgressOutput:(NSString *)output {
3247 performSelectorOnMainThread:@selector(_addProgressOutput:)
3253 - (bool) isCancelling:(size_t)received {
3255 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3256 if (received_ != received) {
3257 received_ = received;
3259 } else if (now - last_ > 30)
3266 - (void) _setConfigurationData:(NSString *)data {
3267 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3269 _assert(conffile_r(data));
3271 NSString *ofile = conffile_r[1];
3272 //NSString *nfile = conffile_r[2];
3274 UIActionSheet *sheet = [[[UIActionSheet alloc]
3275 initWithTitle:@"Configuration Upgrade"
3276 buttons:[NSArray arrayWithObjects:
3277 @"Keep My Old Copy",
3278 @"Accept The New Copy",
3279 // XXX: @"See What Changed",
3281 defaultButtonIndex:0
3286 [sheet setBodyText:[NSString stringWithFormat:
3287 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3290 [sheet popupAlertAnimated:YES];
3293 - (void) _setProgressTitle:(NSString *)title {
3294 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3295 for (size_t i(0), e([words count]); i != e; ++i) {
3296 NSString *word([words objectAtIndex:i]);
3297 if (Package *package = [database_ packageWithName:word])
3298 [words replaceObjectAtIndex:i withObject:[package name]];
3301 [status_ setText:[words componentsJoinedByString:@" "]];
3304 - (void) _setProgressPercent:(NSNumber *)percent {
3305 [progress_ setProgress:[percent floatValue]];
3308 - (void) _addProgressOutput:(NSString *)output {
3309 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3310 CGSize size = [output_ contentSize];
3311 CGRect rect = {{0, size.height}, {size.width, 0}};
3312 [output_ scrollRectToVisible:rect animated:YES];
3315 - (BOOL) isRunning {
3322 /* Package Cell {{{ */
3323 @interface PackageCell : UISimpleTableCell {
3326 NSString *description_;
3330 UITextLabel *status_;
3334 - (PackageCell *) init;
3335 - (void) setPackage:(Package *)package;
3337 + (int) heightForPackage:(Package *)package;
3341 @implementation PackageCell
3343 - (void) clearPackage {
3354 if (description_ != nil) {
3355 [description_ release];
3359 if (source_ != nil) {
3364 if (badge_ != nil) {
3371 [self clearPackage];
3378 - (PackageCell *) init {
3379 if ((self = [super init]) != nil) {
3381 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3382 [status_ setBackgroundColor:[UIColor clearColor]];
3383 [status_ setFont:small];
3388 - (void) setPackage:(Package *)package {
3389 [self clearPackage];
3391 Source *source = [package source];
3392 NSString *section = [package simpleSection];
3394 icon_ = [[package icon] retain];
3396 name_ = [[package name] retain];
3397 description_ = [[package tagline] retain];
3399 NSString *label = nil;
3400 bool trusted = false;
3402 if (source != nil) {
3403 label = [source label];
3404 trusted = [source trusted];
3405 } else if ([[package id] isEqualToString:@"firmware"])
3408 label = @"Unknown/Local";
3410 NSString *from = [NSString stringWithFormat:@"from %@", label];
3412 if (section != nil && ![section isEqualToString:label])
3413 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3415 source_ = [from retain];
3417 if (NSString *purpose = [package primaryPurpose])
3418 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3419 badge_ = [badge_ retain];
3422 if (NSString *mode = [package mode]) {
3423 [badge_ setImage:[UIImage applicationImageNamed:
3424 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3427 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3428 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3429 } else if ([package half]) {
3430 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3431 [status_ setText:@"Package Damaged"];
3432 [status_ setColor:[UIColor redColor]];
3434 [badge_ setImage:nil];
3435 [status_ setText:nil];
3440 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3443 rect.size = [icon_ size];
3445 rect.size.width /= 2;
3446 rect.size.height /= 2;
3448 rect.origin.x = 25 - rect.size.width / 2;
3449 rect.origin.y = 25 - rect.size.height / 2;
3451 [icon_ drawInRect:rect];
3454 if (badge_ != nil) {
3455 CGSize size = [badge_ size];
3457 [badge_ drawAtPoint:CGPointMake(
3458 36 - size.width / 2,
3459 36 - size.height / 2
3468 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3469 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3473 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3475 [super drawContentInRect:rect selected:selected];
3478 + (int) heightForPackage:(Package *)package {
3479 NSString *tagline([package tagline]);
3480 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3482 if ([package hasMode] || [package half])
3491 /* Section Cell {{{ */
3492 @interface SectionCell : UISimpleTableCell {
3497 _UISwitchSlider *switch_;
3502 - (void) setSection:(Section *)section editing:(BOOL)editing;
3506 @implementation SectionCell
3508 - (void) clearSection {
3509 if (section_ != nil) {
3519 if (count_ != nil) {
3526 [self clearSection];
3533 if ((self = [super init]) != nil) {
3534 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3536 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3537 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3541 - (void) onSwitch:(id)sender {
3542 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3543 if (metadata == nil) {
3544 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3545 [Sections_ setObject:metadata forKey:section_];
3549 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3552 - (void) setSection:(Section *)section editing:(BOOL)editing {
3553 if (editing != editing_) {
3555 [switch_ removeFromSuperview];
3557 [self addSubview:switch_];
3561 [self clearSection];
3563 if (section == nil) {
3564 name_ = [@"All Packages" retain];
3567 section_ = [section name];
3568 if (section_ != nil)
3569 section_ = [section_ retain];
3570 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3571 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3574 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3578 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3579 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3586 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3588 CGSize size = [count_ sizeWithFont:Font14_];
3592 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3594 [super drawContentInRect:rect selected:selected];
3600 /* File Table {{{ */
3601 @interface FileTable : RVPage {
3602 _transient Database *database_;
3605 NSMutableArray *files_;
3609 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3610 - (void) setPackage:(Package *)package;
3614 @implementation FileTable
3617 if (package_ != nil)
3626 - (int) numberOfRowsInTable:(UITable *)table {
3627 return files_ == nil ? 0 : [files_ count];
3630 - (float) table:(UITable *)table heightForRow:(int)row {
3634 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3635 if (reusing == nil) {
3636 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3637 UIFont *font = [UIFont systemFontOfSize:16];
3638 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3640 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3644 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3648 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3649 if ((self = [super initWithBook:book]) != nil) {
3650 database_ = database;
3652 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3654 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3655 [self addSubview:list_];
3657 UITableColumn *column = [[[UITableColumn alloc]
3658 initWithTitle:@"Name"
3660 width:[self frame].size.width
3663 [list_ setDataSource:self];
3664 [list_ setSeparatorStyle:1];
3665 [list_ addTableColumn:column];
3666 [list_ setDelegate:self];
3667 [list_ setReusesTableCells:YES];
3671 - (void) setPackage:(Package *)package {
3672 if (package_ != nil) {
3673 [package_ autorelease];
3682 [files_ removeAllObjects];
3684 if (package != nil) {
3685 package_ = [package retain];
3686 name_ = [[package id] retain];
3688 if (NSArray *files = [package files])
3689 [files_ addObjectsFromArray:files];
3691 if ([files_ count] != 0) {
3692 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3693 [files_ removeObjectAtIndex:0];
3694 [files_ sortUsingSelector:@selector(compareByPath:)];
3696 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3697 [stack addObject:@"/"];
3699 for (int i(0), e([files_ count]); i != e; ++i) {
3700 NSString *file = [files_ objectAtIndex:i];
3701 while (![file hasPrefix:[stack lastObject]])
3702 [stack removeLastObject];
3703 NSString *directory = [stack lastObject];
3704 [stack addObject:[file stringByAppendingString:@"/"]];
3705 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3706 ([stack count] - 2) * 3, "",
3707 [file substringFromIndex:[directory length]]
3716 - (void) resetViewAnimated:(BOOL)animated {
3717 [list_ resetViewAnimated:animated];
3720 - (void) reloadData {
3721 [self setPackage:[database_ packageWithName:name_]];
3722 [self reloadButtons];
3725 - (NSString *) title {
3726 return @"Installed Files";
3729 - (NSString *) backButtonTitle {
3735 /* Package View {{{ */
3736 @interface PackageView : BrowserView {
3737 _transient Database *database_;
3740 NSMutableArray *buttons_;
3743 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3744 - (void) setPackage:(Package *)package;
3748 @implementation PackageView
3751 if (package_ != nil)
3759 - (void) _clickButtonWithName:(NSString *)name {
3760 if ([name isEqualToString:@"Install"])
3761 [delegate_ installPackage:package_];
3762 else if ([name isEqualToString:@"Reinstall"])
3763 [delegate_ installPackage:package_];
3764 else if ([name isEqualToString:@"Remove"])
3765 [delegate_ removePackage:package_];
3766 else if ([name isEqualToString:@"Upgrade"])
3767 [delegate_ installPackage:package_];
3768 else _assert(false);
3771 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3772 int count = [buttons_ count];
3773 _assert(count != 0);
3774 _assert(button <= count + 1);
3776 if (count != button - 1)
3777 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3782 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3783 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3784 return [super webView:sender didFinishLoadForFrame:frame];
3787 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3788 [window setValue:package_ forKey:@"package"];
3789 [super webView:sender didClearWindowObject:window forFrame:frame];
3793 - (void) _rightButtonClicked {
3794 /*[super _rightButtonClicked];
3797 int count = [buttons_ count];
3798 _assert(count != 0);
3801 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3803 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3804 [buttons addObjectsFromArray:buttons_];
3805 [buttons addObject:@"Cancel"];
3807 [delegate_ slideUp:[[[UIActionSheet alloc]
3810 defaultButtonIndex:2
3818 - (NSString *) _rightButtonTitle {
3819 int count = [buttons_ count];
3820 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3823 - (NSString *) backButtonTitle {
3827 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3828 if ((self = [super initWithBook:book]) != nil) {
3829 database_ = database;
3830 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3834 - (void) setPackage:(Package *)package {
3835 if (package_ != nil) {
3836 [package_ autorelease];
3845 [buttons_ removeAllObjects];
3847 if (package != nil) {
3848 package_ = [package retain];
3849 name_ = [[package id] retain];
3851 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3853 if ([package_ source] == nil);
3854 else if ([package_ upgradableAndEssential:NO])
3855 [buttons_ addObject:@"Upgrade"];
3856 else if ([package_ installed] == nil)
3857 [buttons_ addObject:@"Install"];
3859 [buttons_ addObject:@"Reinstall"];
3860 if ([package_ installed] != nil)
3861 [buttons_ addObject:@"Remove"];
3865 - (void) reloadData {
3866 [self setPackage:[database_ packageWithName:name_]];
3867 [self reloadButtons];
3872 /* Package Table {{{ */
3873 @interface PackageTable : RVPage {
3874 _transient Database *database_;
3878 NSMutableArray *packages_;
3879 NSMutableArray *sections_;
3880 UISectionList *list_;
3883 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3885 - (void) setDelegate:(id)delegate;
3886 - (void) setObject:(id)object;
3888 - (void) reloadData;
3889 - (void) resetCursor;
3891 - (UISectionList *) list;
3893 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3897 @implementation PackageTable
3900 [list_ setDataSource:nil];
3905 [packages_ release];
3906 [sections_ release];
3911 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3912 return [sections_ count];
3915 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3916 return [[sections_ objectAtIndex:section] name];
3919 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3920 return [[sections_ objectAtIndex:section] row];
3923 - (int) numberOfRowsInTable:(UITable *)table {
3924 return [packages_ count];
3927 - (float) table:(UITable *)table heightForRow:(int)row {
3928 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3931 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3933 reusing = [[[PackageCell alloc] init] autorelease];
3934 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3938 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3942 - (void) tableRowSelected:(NSNotification *)notification {
3943 int row = [[notification object] selectedRow];
3947 Package *package = [packages_ objectAtIndex:row];
3948 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3949 [view setDelegate:delegate_];
3950 [view setPackage:package];
3951 [book_ pushPage:view];
3954 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3955 if ((self = [super initWithBook:book]) != nil) {
3956 database_ = database;
3957 title_ = [title retain];
3959 object_ = object == nil ? nil : [object retain];
3961 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3962 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3964 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3965 [list_ setDataSource:self];
3967 UITableColumn *column = [[[UITableColumn alloc]
3968 initWithTitle:@"Name"
3970 width:[self frame].size.width
3973 UITable *table = [list_ table];
3974 [table setSeparatorStyle:1];
3975 [table addTableColumn:column];
3976 [table setDelegate:self];
3977 [table setReusesTableCells:YES];
3979 [self addSubview:list_];
3982 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3983 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3987 - (void) setDelegate:(id)delegate {
3988 delegate_ = delegate;
3991 - (void) setObject:(id)object {
3997 object_ = [object retain];
4000 - (void) reloadData {
4001 NSArray *packages = [database_ packages];
4003 [packages_ removeAllObjects];
4004 [sections_ removeAllObjects];
4006 for (size_t i(0); i != [packages count]; ++i) {
4007 Package *package([packages objectAtIndex:i]);
4008 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4009 [packages_ addObject:package];
4012 Section *section = nil;
4014 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4015 Package *package = [packages_ objectAtIndex:offset];
4016 NSString *name = [package index];
4018 if (section == nil || ![[section name] isEqualToString:name]) {
4019 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4020 [sections_ addObject:section];
4023 [section addToCount];
4029 - (NSString *) title {
4033 - (void) resetViewAnimated:(BOOL)animated {
4034 [list_ resetViewAnimated:animated];
4037 - (void) resetCursor {
4038 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4041 - (UISectionList *) list {
4045 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4046 [list_ setShouldHideHeaderInShortLists:hide];
4052 /* Add Source View {{{ */
4053 @interface AddSourceView : RVPage {
4054 _transient Database *database_;
4057 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4061 @implementation AddSourceView
4063 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4064 if ((self = [super initWithBook:book]) != nil) {
4065 database_ = database;
4071 /* Source Cell {{{ */
4072 @interface SourceCell : UITableCell {
4075 NSString *description_;
4081 - (SourceCell *) initWithSource:(Source *)source;
4085 @implementation SourceCell
4090 [description_ release];
4095 - (SourceCell *) initWithSource:(Source *)source {
4096 if ((self = [super init]) != nil) {
4098 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4100 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4101 icon_ = [icon_ retain];
4103 origin_ = [[source name] retain];
4104 label_ = [[source uri] retain];
4105 description_ = [[source description] retain];
4109 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4111 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4118 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4122 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4126 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4128 [super drawContentInRect:rect selected:selected];
4133 /* Source Table {{{ */
4134 @interface SourceTable : RVPage {
4135 _transient Database *database_;
4136 UISectionList *list_;
4137 NSMutableArray *sources_;
4138 UIActionSheet *alert_;
4142 UIProgressHUD *hud_;
4145 //NSURLConnection *installer_;
4146 NSURLConnection *trivial_bz2_;
4147 NSURLConnection *trivial_gz_;
4148 //NSURLConnection *automatic_;
4153 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4157 @implementation SourceTable
4159 - (void) _deallocConnection:(NSURLConnection *)connection {
4160 if (connection != nil) {
4161 [connection cancel];
4162 //[connection setDelegate:nil];
4163 [connection release];
4168 [[list_ table] setDelegate:nil];
4169 [list_ setDataSource:nil];
4178 //[self _deallocConnection:installer_];
4179 [self _deallocConnection:trivial_gz_];
4180 [self _deallocConnection:trivial_bz2_];
4181 //[self _deallocConnection:automatic_];
4188 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4189 return offset_ == 0 ? 1 : 2;
4192 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4193 switch (section + (offset_ == 0 ? 1 : 0)) {
4194 case 0: return @"Entered by User";
4195 case 1: return @"Installed by Packages";
4203 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4204 switch (section + (offset_ == 0 ? 1 : 0)) {
4206 case 1: return offset_;
4214 - (int) numberOfRowsInTable:(UITable *)table {
4215 return [sources_ count];
4218 - (float) table:(UITable *)table heightForRow:(int)row {
4219 Source *source = [sources_ objectAtIndex:row];
4220 return [source description] == nil ? 56 : 73;
4223 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4224 Source *source = [sources_ objectAtIndex:row];
4225 // XXX: weird warning, stupid selectors ;P
4226 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4229 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4233 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4237 - (void) tableRowSelected:(NSNotification*)notification {
4238 UITable *table([list_ table]);
4239 int row([table selectedRow]);
4243 Source *source = [sources_ objectAtIndex:row];
4245 PackageTable *packages = [[[PackageTable alloc]
4248 title:[source label]
4249 filter:@selector(isVisibleInSource:)
4253 [packages setDelegate:delegate_];
4255 [book_ pushPage:packages];
4258 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4259 Source *source = [sources_ objectAtIndex:row];
4260 return [source record] != nil;
4263 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4264 [[list_ table] setDeleteConfirmationRow:row];
4267 - (void) table:(UITable *)table deleteRow:(int)row {
4268 Source *source = [sources_ objectAtIndex:row];
4269 [Sources_ removeObjectForKey:[source key]];
4270 [delegate_ syncData];
4273 - (void) _endConnection:(NSURLConnection *)connection {
4274 NSURLConnection **field = NULL;
4275 if (connection == trivial_bz2_)
4276 field = &trivial_bz2_;
4277 else if (connection == trivial_gz_)
4278 field = &trivial_gz_;
4279 _assert(field != NULL);
4280 [connection release];
4284 trivial_bz2_ == nil &&
4287 [delegate_ setStatusBarShowsProgress:NO];
4290 [hud_ removeFromSuperview];
4295 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4298 @"./", @"Distribution",
4299 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4301 [delegate_ syncData];
4302 } else if (error_ != nil) {
4303 UIActionSheet *sheet = [[[UIActionSheet alloc]
4304 initWithTitle:@"Verification Error"
4305 buttons:[NSArray arrayWithObjects:@"OK", nil]
4306 defaultButtonIndex:0
4311 [sheet setBodyText:[error_ localizedDescription]];
4312 [sheet popupAlertAnimated:YES];
4314 UIActionSheet *sheet = [[[UIActionSheet alloc]
4315 initWithTitle:@"Did not Find Repository"
4316 buttons:[NSArray arrayWithObjects:@"OK", nil]
4317 defaultButtonIndex:0
4322 [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."];
4323 [sheet popupAlertAnimated:YES];
4329 if (error_ != nil) {
4336 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4337 switch ([response statusCode]) {
4343 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4344 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4346 error_ = [error retain];
4347 [self _endConnection:connection];
4350 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4351 [self _endConnection:connection];
4354 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4355 NSMutableURLRequest *request = [NSMutableURLRequest
4356 requestWithURL:[NSURL URLWithString:href]
4357 cachePolicy:NSURLRequestUseProtocolCachePolicy
4358 timeoutInterval:20.0
4361 [request setHTTPMethod:method];
4363 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4366 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4367 NSString *context = [sheet context];
4368 if ([context isEqualToString:@"source"])
4371 NSString *href = [[sheet textField] text];
4373 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4375 if (![href hasSuffix:@"/"])
4376 href_ = [href stringByAppendingString:@"/"];
4379 href_ = [href_ retain];
4381 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4382 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4383 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4387 hud_ = [delegate_ addProgressHUD];
4388 [hud_ setText:@"Verifying URL"];
4401 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4402 if ((self = [super initWithBook:book]) != nil) {
4403 database_ = database;
4404 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4406 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4407 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4408 [list_ setShouldHideHeaderInShortLists:NO];
4410 [self addSubview:list_];
4411 [list_ setDataSource:self];
4413 UITableColumn *column = [[UITableColumn alloc]
4414 initWithTitle:@"Name"
4416 width:[self frame].size.width
4419 UITable *table = [list_ table];
4420 [table setSeparatorStyle:1];
4421 [table addTableColumn:column];
4422 [table setDelegate:self];
4426 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4427 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4431 - (void) reloadData {
4433 _assert(list.ReadMainList());
4435 [sources_ removeAllObjects];
4436 [sources_ addObjectsFromArray:[database_ sources]];
4438 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4441 int count = [sources_ count];
4442 for (offset_ = 0; offset_ != count; ++offset_) {
4443 Source *source = [sources_ objectAtIndex:offset_];
4444 if ([source record] == nil)
4451 - (void) resetViewAnimated:(BOOL)animated {
4452 [list_ resetViewAnimated:animated];
4455 - (void) _leftButtonClicked {
4456 /*[book_ pushPage:[[[AddSourceView alloc]
4461 UIActionSheet *sheet = [[[UIActionSheet alloc]
4462 initWithTitle:@"Enter Cydia/APT URL"
4463 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4464 defaultButtonIndex:0
4469 [sheet addTextFieldWithValue:@"http://" label:@""];
4471 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4472 [traits setAutocapitalizationType:0];
4473 [traits setKeyboardType:3];
4474 [traits setAutocorrectionType:1];
4476 [sheet popupAlertAnimated:YES];
4479 - (void) _rightButtonClicked {
4480 UITable *table = [list_ table];
4481 BOOL editing = [table isRowDeletionEnabled];
4482 [table enableRowDeletion:!editing animated:YES];
4483 [book_ reloadButtonsForPage:self];
4486 - (NSString *) title {
4490 - (NSString *) leftButtonTitle {
4491 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4494 - (NSString *) rightButtonTitle {
4495 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4498 - (UINavigationButtonStyle) rightButtonStyle {
4499 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4505 /* Installed View {{{ */
4506 @interface InstalledView : RVPage {
4507 _transient Database *database_;
4508 PackageTable *packages_;
4512 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4516 @implementation InstalledView
4519 [packages_ release];
4523 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4524 if ((self = [super initWithBook:book]) != nil) {
4525 database_ = database;
4527 packages_ = [[PackageTable alloc]
4531 filter:@selector(isInstalledAndVisible:)
4532 with:[NSNumber numberWithBool:YES]
4535 [self addSubview:packages_];
4537 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4538 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4542 - (void) resetViewAnimated:(BOOL)animated {
4543 [packages_ resetViewAnimated:animated];
4546 - (void) reloadData {
4547 [packages_ reloadData];
4550 - (void) _rightButtonClicked {
4551 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4552 [packages_ reloadData];
4554 [book_ reloadButtonsForPage:self];
4557 - (NSString *) title {
4558 return @"Installed";
4561 - (NSString *) backButtonTitle {
4565 - (NSString *) rightButtonTitle {
4566 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4569 - (UINavigationButtonStyle) rightButtonStyle {
4570 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4573 - (void) setDelegate:(id)delegate {
4574 [super setDelegate:delegate];
4575 [packages_ setDelegate:delegate];
4582 @interface HomeView : BrowserView {
4587 @implementation HomeView
4589 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4593 - (void) _leftButtonClicked {
4594 UIActionSheet *sheet = [[[UIActionSheet alloc]
4595 initWithTitle:@"About Cydia Installer"
4596 buttons:[NSArray arrayWithObjects:@"Close", nil]
4597 defaultButtonIndex:0
4603 @"Copyright (C) 2008\n"
4604 "Jay Freeman (saurik)\n"
4605 "saurik@saurik.com\n"
4606 "http://www.saurik.com/\n"
4609 "http://www.theokorigroup.com/\n"
4611 "College of Creative Studies,\n"
4612 "University of California,\n"
4614 "http://www.ccs.ucsb.edu/"
4617 [sheet popupAlertAnimated:YES];
4620 - (NSString *) leftButtonTitle {
4626 /* Manage View {{{ */
4627 @interface ManageView : BrowserView {
4632 @implementation ManageView
4634 - (NSString *) title {
4638 - (void) _leftButtonClicked {
4639 [delegate_ askForSettings];
4642 - (NSString *) leftButtonTitle {
4646 - (NSString *) _rightButtonTitle {
4653 @interface WebView (Cydia)
4654 - (void) setScriptDebugDelegate:(id)delegate;
4655 - (void) _setFormDelegate:(id)delegate;
4656 - (void) _setUIKitDelegate:(id)delegate;
4657 - (void) setWebMailDelegate:(id)delegate;
4658 - (void) _setLayoutInterval:(float)interval;
4661 /* Indirect Delegate {{{ */
4662 @interface IndirectDelegate : NSProxy {
4663 _transient volatile id delegate_;
4666 - (void) setDelegate:(id)delegate;
4667 - (id) initWithDelegate:(id)delegate;
4670 @implementation IndirectDelegate
4672 - (void) setDelegate:(id)delegate {
4673 delegate_ = delegate;
4676 - (id) initWithDelegate:(id)delegate {
4677 delegate_ = delegate;
4681 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4682 if (delegate_ != nil)
4683 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4685 // XXX: I fucking hate Apple so very very bad
4686 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4689 - (void) forwardInvocation:(NSInvocation *)inv {
4690 SEL sel = [inv selector];
4691 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4692 [inv invokeWithTarget:delegate_];
4697 /* Browser Implementation {{{ */
4698 @implementation BrowserView
4701 WebView *webview = [webview_ webView];
4702 [webview setFrameLoadDelegate:nil];
4703 [webview setResourceLoadDelegate:nil];
4704 [webview setUIDelegate:nil];
4705 [webview setScriptDebugDelegate:nil];
4706 [webview setPolicyDelegate:nil];
4708 [webview setDownloadDelegate:nil];
4710 [webview _setFormDelegate:nil];
4711 [webview _setUIKitDelegate:nil];
4712 [webview setWebMailDelegate:nil];
4713 [webview setEditingDelegate:nil];
4715 [webview_ setDelegate:nil];
4716 [webview_ setGestureDelegate:nil];
4718 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4723 [webview_ removeFromSuperview];
4724 [Documents_ addObject:[webview_ autorelease]];
4729 [indirect_ setDelegate:nil];
4730 [indirect_ release];
4732 [scroller_ setDelegate:nil];
4734 [scroller_ release];
4736 [indicator_ release];
4742 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4743 [self loadRequest:[NSURLRequest
4746 timeoutInterval:30.0
4750 - (void) loadURL:(NSURL *)url {
4751 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4754 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4755 NSMutableURLRequest *copy = [request mutableCopy];
4757 if (Machine_ != NULL)
4758 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4759 if (UniqueID_ != nil)
4760 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4763 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4768 - (void) loadRequest:(NSURLRequest *)request {
4770 [webview_ loadRequest:request];
4773 - (void) reloadURL {
4774 if ([urls_ count] == 0)
4776 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4777 [urls_ removeLastObject];
4778 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4781 - (WebView *) webView {
4782 return [webview_ webView];
4785 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4786 [scroller_ setContentSize:frame.size];
4789 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4790 [self view:sender didSetFrame:frame];
4793 - (void) pushPage:(RVPage *)page {
4794 [self setBackButtonTitle:title_];
4795 [page setDelegate:delegate_];
4796 [book_ pushPage:page];
4799 - (BOOL) getSpecial:(NSURL *)url {
4800 NSString *href([url absoluteString]);
4801 NSString *scheme([[url scheme] lowercaseString]);
4805 if ([href hasPrefix:@"apptapp://package/"])
4806 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4807 else if ([scheme isEqualToString:@"cydia"]) {
4808 page = [delegate_ pageForURL:url hasTag:NULL];
4811 } else if (![scheme isEqualToString:@"apptapp"])
4815 [self pushPage:page];
4819 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4820 [window setValue:delegate_ forKey:@"cydia"];
4823 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4824 if (NSURL *url = [request URL]) {
4825 if (![self getSpecial:url]) {
4826 NSString *scheme([[url scheme] lowercaseString]);
4827 if ([scheme isEqualToString:@"mailto"])
4828 [delegate_ openMailToURL:url];
4837 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4838 if ([WebView canShowMIMEType:type])
4841 // XXX: handle more mime types!
4843 if (frame == [webView mainFrame])
4844 [UIApp openURL:[request URL]];
4848 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4849 NSURL *url([request URL]);
4851 if (url == nil) use: {
4856 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4859 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4860 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4863 [UIApp openURL:url];
4869 int store(_not(int));
4870 if (NSURL *itms = [url itmsURL:&store]) {
4871 NSLog(@"itms#%@#%u#%@", url, store, itms);
4873 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4874 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4881 NSString *scheme([[url scheme] lowercaseString]);
4883 if ([scheme isEqualToString:@"tel"]) {
4884 // XXX: intelligence
4888 if ([scheme isEqualToString:@"mailto"]) {
4889 [delegate_ openMailToURL:url];
4893 if ([self getSpecial:url])
4895 else if ([WebView _canHandleRequest:request])
4897 else if ([url isSpringboardHandledURL])
4903 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4904 //lprintf("Status:%s\n", [text UTF8String]);
4907 - (void) _pushPage {
4911 [book_ pushPage:self];
4914 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4915 NSURL *url = [request URL];
4916 if ([self getSpecial:url])
4919 return [self _addHeadersToRequest:request];
4922 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4923 [self setBackButtonTitle:title_];
4925 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4926 [browser setDelegate:delegate_];
4929 [browser loadRequest:[self _addHeadersToRequest:request]];
4930 [book_ pushPage:browser];
4933 return [browser webView];
4936 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4937 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4940 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4941 return [self _createWebViewWithRequest:request pushed:YES];
4944 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4945 if ([frame parentFrame] != nil)
4948 title_ = [title retain];
4949 [book_ reloadTitleForPage:self];
4952 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4953 if ([frame parentFrame] != nil)
4958 [indicator_ startAnimation];
4959 [self reloadButtons];
4961 if (title_ != nil) {
4966 [book_ reloadTitleForPage:self];
4968 WebView *webview = [webview_ webView];
4969 NSString *href = [webview mainFrameURL];
4970 [urls_ addObject:[NSURL URLWithString:href]];
4972 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4974 CGRect webrect = [scroller_ bounds];
4975 webrect.size.height = 0;
4976 [webview_ setFrame:webrect];
4979 - (void) _finishLoading {
4982 [indicator_ stopAnimation];
4983 [self reloadButtons];
4987 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4988 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4991 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4992 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4995 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4996 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4999 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5000 return [webview_ webView:sender didCommitLoadForFrame:frame];
5003 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5004 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5007 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5008 if ([frame parentFrame] == nil)
5009 [self _finishLoading];
5010 return [webview_ webView:sender didFinishLoadForFrame:frame];
5013 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5014 if ([frame parentFrame] != nil)
5016 [self _finishLoading];
5018 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5019 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5020 [[error localizedDescription] stringByAddingPercentEscapes]
5024 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5026 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5030 - (id) initWithBook:(RVBook *)book {
5031 if ((self = [super initWithBook:book]) != nil) {
5034 struct CGRect bounds = [self bounds];
5036 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5037 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5038 [self addSubview:pinstripe];
5040 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5041 [self addSubview:scroller_];
5043 [scroller_ setScrollingEnabled:YES];
5044 [scroller_ setAdjustForContentSizeChange:YES];
5045 [scroller_ setClipsSubviews:YES];
5046 [scroller_ setAllowsRubberBanding:YES];
5047 [scroller_ setScrollDecelerationFactor:0.99];
5048 [scroller_ setDelegate:self];
5050 CGRect webrect = [scroller_ bounds];
5051 webrect.size.height = 0;
5056 webview_ = [Documents_ lastObject];
5057 if (webview_ != nil) {
5058 webview_ = [webview_ retain];
5059 webview = [webview_ webView];
5060 [Documents_ removeLastObject];
5061 [webview_ setFrame:webrect];
5066 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5067 webview = [webview_ webView];
5069 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5071 [webview_ setAllowsMessaging:YES];
5073 [webview_ setTilingEnabled:YES];
5074 [webview_ setDrawsGrid:NO];
5075 [webview_ setLogsTilingChanges:NO];
5076 [webview_ setTileMinificationFilter:kCAFilterNearest];
5077 [webview_ setDetectsPhoneNumbers:NO];
5078 [webview_ setAutoresizes:YES];
5080 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5081 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5082 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5084 [webview_ _setDocumentType:0x4];
5086 [webview_ setZoomsFocusedFormControl:YES];
5087 [webview_ setContentsPosition:7];
5088 [webview_ setEnabledGestures:0xa];
5089 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5090 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5092 [webview_ setSmoothsFonts:YES];
5094 [webview _setUsesLoaderCache:YES];
5095 [webview setGroupName:@"Cydia"];
5096 //[webview _setLayoutInterval:0.5];
5099 [webview_ setDelegate:self];
5100 [webview_ setGestureDelegate:self];
5101 [scroller_ addSubview:webview_];
5103 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5105 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5106 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5107 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5109 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5110 NSString *application = package == nil ? @"Cydia" : [NSString
5111 stringWithFormat:@"Cydia/%@",
5113 ]; [webview setApplicationNameForUserAgent:application];
5115 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5117 [webview setFrameLoadDelegate:self];
5118 [webview setResourceLoadDelegate:indirect_];
5119 [webview setUIDelegate:self];
5120 [webview setScriptDebugDelegate:self];
5121 [webview setPolicyDelegate:self];
5123 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5125 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5126 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5127 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5131 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5132 [webview_ redrawScaledDocument];
5135 - (void) _rightButtonClicked {
5140 - (NSString *) _rightButtonTitle {
5144 - (NSString *) rightButtonTitle {
5145 return loading_ ? @"" : [self _rightButtonTitle];
5148 - (NSString *) title {
5149 return title_ == nil ? @"Loading" : title_;
5152 - (NSString *) backButtonTitle {
5156 - (void) setPageActive:(BOOL)active {
5158 [indicator_ removeFromSuperview];
5160 [[book_ navigationBar] addSubview:indicator_];
5163 - (void) resetViewAnimated:(BOOL)animated {
5166 - (void) setPushed:(bool)pushed {
5173 /* Cydia Book {{{ */
5174 @interface CYBook : RVBook <
5177 _transient Database *database_;
5178 UINavigationBar *overlay_;
5179 UINavigationBar *underlay_;
5180 UIProgressIndicator *indicator_;
5181 UITextLabel *prompt_;
5182 UIProgressBar *progress_;
5183 UINavigationButton *cancel_;
5186 NSTimeInterval last_;
5189 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5195 @implementation CYBook
5199 [indicator_ release];
5201 [progress_ release];
5206 - (NSString *) getTitleForPage:(RVPage *)page {
5207 return Simplify([super getTitleForPage:page]);
5215 [UIView beginAnimations:nil context:NULL];
5217 CGRect ovrframe = [overlay_ frame];
5218 ovrframe.origin.y = 0;
5219 [overlay_ setFrame:ovrframe];
5221 CGRect barframe = [navbar_ frame];
5222 barframe.origin.y += ovrframe.size.height;
5223 [navbar_ setFrame:barframe];
5225 CGRect trnframe = [transition_ frame];
5226 trnframe.origin.y += ovrframe.size.height;
5227 trnframe.size.height -= ovrframe.size.height;
5228 [transition_ setFrame:trnframe];
5230 [UIView endAnimations];
5232 [indicator_ startAnimation];
5233 [prompt_ setText:@"Updating Database"];
5234 [progress_ setProgress:0];
5237 last_ = [NSDate timeIntervalSinceReferenceDate];
5239 [overlay_ addSubview:cancel_];
5242 detachNewThreadSelector:@selector(_update)
5251 [indicator_ stopAnimation];
5253 [UIView beginAnimations:nil context:NULL];
5255 CGRect ovrframe = [overlay_ frame];
5256 ovrframe.origin.y = -ovrframe.size.height;
5257 [overlay_ setFrame:ovrframe];
5259 CGRect barframe = [navbar_ frame];
5260 barframe.origin.y -= ovrframe.size.height;
5261 [navbar_ setFrame:barframe];
5263 CGRect trnframe = [transition_ frame];
5264 trnframe.origin.y -= ovrframe.size.height;
5265 trnframe.size.height += ovrframe.size.height;
5266 [transition_ setFrame:trnframe];
5268 [UIView commitAnimations];
5270 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5273 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5274 if ((self = [super initWithFrame:frame]) != nil) {
5275 database_ = database;
5277 CGRect ovrrect = [navbar_ bounds];
5278 ovrrect.size.height = [UINavigationBar defaultSize].height;
5279 ovrrect.origin.y = -ovrrect.size.height;
5281 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5282 [self addSubview:overlay_];
5284 ovrrect.origin.y = frame.size.height;
5285 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5286 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5287 [self addSubview:underlay_];
5289 [overlay_ setBarStyle:1];
5290 [underlay_ setBarStyle:1];
5292 int barstyle = [overlay_ _barStyle:NO];
5293 bool ugly = barstyle == 0;
5295 UIProgressIndicatorStyle style = ugly ?
5296 UIProgressIndicatorStyleMediumBrown :
5297 UIProgressIndicatorStyleMediumWhite;
5299 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5300 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5301 CGRect indrect = {{indoffset, indoffset}, indsize};
5303 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5304 [indicator_ setStyle:style];
5305 [overlay_ addSubview:indicator_];
5307 CGSize prmsize = {215, indsize.height + 4};
5310 indoffset * 2 + indsize.width,
5314 unsigned(ovrrect.size.height - prmsize.height) / 2
5317 UIFont *font = [UIFont systemFontOfSize:15];
5319 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5321 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5322 [prompt_ setBackgroundColor:[UIColor clearColor]];
5323 [prompt_ setFont:font];
5325 [overlay_ addSubview:prompt_];
5327 CGSize prgsize = {75, 100};
5330 ovrrect.size.width - prgsize.width - 10,
5331 (ovrrect.size.height - prgsize.height) / 2
5334 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5335 [progress_ setStyle:0];
5336 [overlay_ addSubview:progress_];
5338 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5339 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5341 CGRect frame = [cancel_ frame];
5342 frame.size.width = 65;
5343 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5344 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5345 [cancel_ setFrame:frame];
5347 [cancel_ setBarStyle:barstyle];
5351 - (void) _onCancel {
5353 [cancel_ removeFromSuperview];
5356 - (void) _update { _pooled
5358 status.setDelegate(self);
5360 [database_ updateWithStatus:status];
5363 performSelectorOnMainThread:@selector(_update_)
5369 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5370 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5373 - (void) setProgressTitle:(NSString *)title {
5375 performSelectorOnMainThread:@selector(_setProgressTitle:)
5381 - (void) setProgressPercent:(float)percent {
5383 performSelectorOnMainThread:@selector(_setProgressPercent:)
5384 withObject:[NSNumber numberWithFloat:percent]
5389 - (void) startProgress {
5392 - (void) addProgressOutput:(NSString *)output {
5394 performSelectorOnMainThread:@selector(_addProgressOutput:)
5400 - (bool) isCancelling:(size_t)received {
5401 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5402 if (received_ != received) {
5403 received_ = received;
5405 } else if (now - last_ > 15)
5410 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5414 - (void) _setProgressTitle:(NSString *)title {
5415 [prompt_ setText:title];
5418 - (void) _setProgressPercent:(NSNumber *)percent {
5419 [progress_ setProgress:[percent floatValue]];
5422 - (void) _addProgressOutput:(NSString *)output {
5427 /* Cydia:// Protocol {{{ */
5428 @interface CydiaURLProtocol : NSURLProtocol {
5433 @implementation CydiaURLProtocol
5435 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5436 NSURL *url([request URL]);
5439 NSString *scheme([[url scheme] lowercaseString]);
5440 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5445 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5449 - (void) startLoading {
5450 id<NSURLProtocolClient> client([self client]);
5451 NSURLRequest *request([self request]);
5453 NSURL *url([request URL]);
5454 NSString *href([url absoluteString]);
5456 NSString *path([href substringFromIndex:8]);
5457 NSRange slash([path rangeOfString:@"/"]);
5460 if (slash.location == NSNotFound) {
5464 command = [path substringToIndex:slash.location];
5465 path = [path substringFromIndex:(slash.location + 1)];
5468 Database *database([Database sharedInstance]);
5470 if ([command isEqualToString:@"package-icon"]) {
5473 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5474 Package *package([database packageWithName:path]);
5478 UIImage *icon([package icon]);
5480 NSData *data(UIImagePNGRepresentation(icon));
5482 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5483 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5484 [client URLProtocol:self didLoadData:data];
5485 [client URLProtocolDidFinishLoading:self];
5486 } else if ([command isEqualToString:@"section-icon"]) {
5489 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5490 NSString *section(Simplify(path));
5492 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5494 icon = [UIImage applicationImageNamed:@"unknown.png"];
5496 NSData *data(UIImagePNGRepresentation(icon));
5498 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5499 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5500 [client URLProtocol:self didLoadData:data];
5501 [client URLProtocolDidFinishLoading:self];
5503 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5507 - (void) stopLoading {
5513 /* Install View {{{ */
5514 @interface InstallView : RVPage {
5515 _transient Database *database_;
5516 NSMutableArray *sections_;
5517 NSMutableArray *filtered_;
5518 UITransitionView *transition_;
5524 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5525 - (void) reloadData;
5530 @implementation InstallView
5533 [list_ setDataSource:nil];
5534 [list_ setDelegate:nil];
5536 [sections_ release];
5537 [filtered_ release];
5538 [transition_ release];
5540 [accessory_ release];
5544 - (int) numberOfRowsInTable:(UITable *)table {
5545 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5548 - (float) table:(UITable *)table heightForRow:(int)row {
5552 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5554 reusing = [[[SectionCell alloc] init] autorelease];
5555 [(SectionCell *)reusing setSection:(editing_ ?
5556 [sections_ objectAtIndex:row] :
5557 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5558 ) editing:editing_];
5562 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5566 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5570 - (void) tableRowSelected:(NSNotification *)notification {
5571 int row = [[notification object] selectedRow];
5582 title = @"All Packages";
5584 section = [filtered_ objectAtIndex:(row - 1)];
5585 name = [section name];
5591 title = @"(No Section)";
5595 PackageTable *table = [[[PackageTable alloc]
5599 filter:@selector(isVisiblyUninstalledInSection:)
5603 [table setDelegate:delegate_];
5605 [book_ pushPage:table];
5608 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5609 if ((self = [super initWithBook:book]) != nil) {
5610 database_ = database;
5612 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5613 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5615 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5616 [self addSubview:transition_];
5618 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5619 [transition_ transition:0 toView:list_];
5621 UITableColumn *column = [[[UITableColumn alloc]
5622 initWithTitle:@"Name"
5624 width:[self frame].size.width
5627 [list_ setDataSource:self];
5628 [list_ setSeparatorStyle:1];
5629 [list_ addTableColumn:column];
5630 [list_ setDelegate:self];
5631 [list_ setReusesTableCells:YES];
5635 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5636 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5640 - (void) reloadData {
5641 NSArray *packages = [database_ packages];
5643 [sections_ removeAllObjects];
5644 [filtered_ removeAllObjects];
5646 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5647 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5650 for (size_t i(0); i != [packages count]; ++i) {
5651 Package *package([packages objectAtIndex:i]);
5652 NSString *name([package section]);
5655 Section *section([sections objectForKey:name]);
5656 if (section == nil) {
5657 section = [[[Section alloc] initWithName:name] autorelease];
5658 [sections setObject:section forKey:name];
5662 if ([package valid] && [package installed] == nil && [package visible])
5663 [filtered addObject:package];
5667 [sections_ addObjectsFromArray:[sections allValues]];
5668 [sections_ sortUsingSelector:@selector(compareByName:)];
5671 [filtered sortUsingSelector:@selector(compareBySection:)];
5674 Section *section = nil;
5675 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5676 Package *package = [filtered objectAtIndex:offset];
5677 NSString *name = [package section];
5679 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5680 section = name == nil ?
5681 [[[Section alloc] initWithName:nil] autorelease] :
5682 [sections objectForKey:name];
5683 [filtered_ addObject:section];
5686 [section addToCount];
5694 - (void) resetView {
5696 [self _rightButtonClicked];
5699 - (void) resetViewAnimated:(BOOL)animated {
5700 [list_ resetViewAnimated:animated];
5703 - (void) _rightButtonClicked {
5704 if ((editing_ = !editing_))
5707 [delegate_ updateData];
5708 [book_ reloadTitleForPage:self];
5709 [book_ reloadButtonsForPage:self];
5712 - (NSString *) title {
5713 return editing_ ? @"Section Visibility" : @"Install by Section";
5716 - (NSString *) backButtonTitle {
5720 - (NSString *) rightButtonTitle {
5721 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5724 - (UINavigationButtonStyle) rightButtonStyle {
5725 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5728 - (UIView *) accessoryView {
5734 /* Changes View {{{ */
5735 @interface ChangesView : RVPage {
5736 _transient Database *database_;
5737 NSMutableArray *packages_;
5738 NSMutableArray *sections_;
5739 UISectionList *list_;
5743 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5744 - (void) reloadData;
5748 @implementation ChangesView
5751 [[list_ table] setDelegate:nil];
5752 [list_ setDataSource:nil];
5754 [packages_ release];
5755 [sections_ release];
5760 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5761 return [sections_ count];
5764 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5765 return [[sections_ objectAtIndex:section] name];
5768 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5769 return [[sections_ objectAtIndex:section] row];
5772 - (int) numberOfRowsInTable:(UITable *)table {
5773 return [packages_ count];
5776 - (float) table:(UITable *)table heightForRow:(int)row {
5777 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5780 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5782 reusing = [[[PackageCell alloc] init] autorelease];
5783 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5787 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5791 - (void) tableRowSelected:(NSNotification *)notification {
5792 int row = [[notification object] selectedRow];
5795 Package *package = [packages_ objectAtIndex:row];
5796 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5797 [view setDelegate:delegate_];
5798 [view setPackage:package];
5799 [book_ pushPage:view];
5802 - (void) _leftButtonClicked {
5803 [(CYBook *)book_ update];
5804 [self reloadButtons];
5807 - (void) _rightButtonClicked {
5808 [delegate_ distUpgrade];
5811 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5812 if ((self = [super initWithBook:book]) != nil) {
5813 database_ = database;
5815 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5816 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5818 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5819 [self addSubview:list_];
5821 [list_ setShouldHideHeaderInShortLists:NO];
5822 [list_ setDataSource:self];
5823 //[list_ setSectionListStyle:1];
5825 UITableColumn *column = [[[UITableColumn alloc]
5826 initWithTitle:@"Name"
5828 width:[self frame].size.width
5831 UITable *table = [list_ table];
5832 [table setSeparatorStyle:1];
5833 [table addTableColumn:column];
5834 [table setDelegate:self];
5835 [table setReusesTableCells:YES];
5839 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5840 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5844 - (void) reloadData {
5845 NSArray *packages = [database_ packages];
5847 [packages_ removeAllObjects];
5848 [sections_ removeAllObjects];
5851 for (size_t i(0); i != [packages count]; ++i) {
5852 Package *package([packages objectAtIndex:i]);
5855 [package installed] == nil && [package valid] && [package visible] ||
5856 [package upgradableAndEssential:NO]
5858 [packages_ addObject:package];
5862 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5865 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5866 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5867 Section *section = nil;
5871 bool unseens = false;
5873 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5876 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5877 Package *package = [packages_ objectAtIndex:offset];
5879 if (![package upgradableAndEssential:YES]) {
5881 NSDate *seen = [package seen];
5883 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5886 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5887 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5888 [sections_ addObject:section];
5892 [section addToCount];
5893 } else if ([package ignored])
5894 [ignored addToCount];
5897 [upgradable addToCount];
5902 CFRelease(formatter);
5905 Section *last = [sections_ lastObject];
5906 size_t count = [last count];
5907 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5908 [sections_ removeLastObject];
5911 if ([ignored count] != 0)
5912 [sections_ insertObject:ignored atIndex:0];
5914 [sections_ insertObject:upgradable atIndex:0];
5917 [self reloadButtons];
5920 - (void) resetViewAnimated:(BOOL)animated {
5921 [list_ resetViewAnimated:animated];
5924 - (NSString *) leftButtonTitle {
5925 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5928 - (NSString *) rightButtonTitle {
5929 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5932 - (NSString *) title {
5938 /* Search View {{{ */
5939 @protocol SearchViewDelegate
5940 - (void) showKeyboard:(BOOL)show;
5943 @interface SearchView : RVPage {
5945 UISearchField *field_;
5946 UITransitionView *transition_;
5947 PackageTable *table_;
5948 UIPreferencesTable *advanced_;
5954 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5955 - (void) reloadData;
5959 @implementation SearchView
5962 [field_ setDelegate:nil];
5964 [accessory_ release];
5966 [transition_ release];
5968 [advanced_ release];
5973 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5977 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5979 case 0: return @"Advanced Search (Coming Soon!)";
5981 default: _assert(false);
5985 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5989 default: _assert(false);
5993 - (void) _showKeyboard:(BOOL)show {
5994 CGSize keysize = [UIKeyboard defaultSize];
5995 CGRect keydown = [book_ pageBounds];
5996 CGRect keyup = keydown;
5997 keyup.size.height -= keysize.height - ButtonBarHeight_;
5999 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6001 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6002 [animation setSignificantRectFields:8];
6005 [animation setStartFrame:keydown];
6006 [animation setEndFrame:keyup];
6008 [animation setStartFrame:keyup];
6009 [animation setEndFrame:keydown];
6012 UIAnimator *animator = [UIAnimator sharedAnimator];
6015 addAnimations:[NSArray arrayWithObjects:animation, nil]
6016 withDuration:(KeyboardTime_ - delay)
6021 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6023 [delegate_ showKeyboard:show];
6026 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6027 [self _showKeyboard:YES];
6030 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6031 [self _showKeyboard:NO];
6034 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6036 NSString *text([field_ text]);
6037 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6043 - (void) textFieldClearButtonPressed:(UITextField *)field {
6047 - (void) keyboardInputShouldDelete:(id)input {
6051 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6052 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6056 [field_ resignFirstResponder];
6061 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6062 if ((self = [super initWithBook:book]) != nil) {
6063 CGRect pageBounds = [book_ pageBounds];
6065 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6066 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6067 [self addSubview:pinstripe];*/
6069 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6070 [self addSubview:transition_];
6072 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6074 [advanced_ setReusesTableCells:YES];
6075 [advanced_ setDataSource:self];
6076 [advanced_ reloadData];
6078 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6079 CGColor dimmed(space_, 0, 0, 0, 0.5);
6080 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6082 table_ = [[PackageTable alloc]
6086 filter:@selector(isUnfilteredAndSearchedForBy:)
6090 [table_ setShouldHideHeaderInShortLists:NO];
6091 [transition_ transition:0 toView:table_];
6100 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6107 [self bounds].size.width - area.origin.x - 18;
6109 area.size.height = [UISearchField defaultHeight];
6111 field_ = [[UISearchField alloc] initWithFrame:area];
6113 UIFont *font = [UIFont systemFontOfSize:16];
6114 [field_ setFont:font];
6116 [field_ setPlaceholder:@"Package Names & Descriptions"];
6117 [field_ setDelegate:self];
6119 [field_ setPaddingTop:5];
6121 UITextInputTraits *traits = [field_ textInputTraits];
6122 [traits setAutocapitalizationType:0];
6123 [traits setAutocorrectionType:1];
6124 [traits setReturnKeyType:6];
6126 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6128 accessory_ = [[UIView alloc] initWithFrame:accrect];
6129 [accessory_ addSubview:field_];
6131 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6132 [configure setShowPressFeedback:YES];
6133 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6134 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6135 [accessory_ addSubview:configure];*/
6137 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6138 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6144 LKAnimation *animation = [LKTransition animation];
6145 [animation setType:@"oglFlip"];
6146 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6147 [animation setFillMode:@"extended"];
6148 [animation setTransitionFlags:3];
6149 [animation setDuration:10];
6150 [animation setSpeed:0.35];
6151 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6152 [[transition_ _layer] addAnimation:animation forKey:0];
6153 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6154 flipped_ = !flipped_;
6158 - (void) configurePushed {
6159 [field_ resignFirstResponder];
6163 - (void) resetViewAnimated:(BOOL)animated {
6166 [table_ resetViewAnimated:animated];
6169 - (void) _reloadData {
6172 - (void) reloadData {
6175 [table_ setObject:[field_ text]];
6176 [table_ reloadData];
6177 [table_ resetCursor];
6180 - (UIView *) accessoryView {
6184 - (NSString *) title {
6188 - (NSString *) backButtonTitle {
6192 - (void) setDelegate:(id)delegate {
6193 [table_ setDelegate:delegate];
6194 [super setDelegate:delegate];
6200 @interface SettingsView : RVPage {
6201 _transient Database *database_;
6204 UIPreferencesTable *table_;
6205 _UISwitchSlider *subscribedSwitch_;
6206 _UISwitchSlider *ignoredSwitch_;
6207 UIPreferencesControlTableCell *subscribedCell_;
6208 UIPreferencesControlTableCell *ignoredCell_;
6211 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6215 @implementation SettingsView
6218 [table_ setDataSource:nil];
6221 if (package_ != nil)
6224 [subscribedSwitch_ release];
6225 [ignoredSwitch_ release];
6226 [subscribedCell_ release];
6227 [ignoredCell_ release];
6231 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6232 if (package_ == nil)
6238 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6239 if (package_ == nil)
6246 default: _assert(false);
6252 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6253 if (package_ == nil)
6260 default: _assert(false);
6266 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6267 if (package_ == nil)
6274 default: _assert(false);
6280 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6281 if (package_ == nil)
6284 _UISwitchSlider *slider([cell control]);
6285 BOOL value([slider value] != 0);
6286 NSMutableDictionary *metadata([package_ metadata]);
6289 if (NSNumber *number = [metadata objectForKey:key])
6290 before = [number boolValue];
6294 if (value != before) {
6295 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6297 [delegate_ updateData];
6301 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6302 [self onSomething:cell withKey:@"IsSubscribed"];
6305 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6306 [self onSomething:cell withKey:@"IsIgnored"];
6309 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6310 if (package_ == nil)
6314 case 0: switch (row) {
6316 return subscribedCell_;
6318 return ignoredCell_;
6319 default: _assert(false);
6322 case 1: switch (row) {
6324 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6325 [cell setShowSelection:NO];
6326 [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."];
6330 default: _assert(false);
6333 default: _assert(false);
6339 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6340 if ((self = [super initWithBook:book])) {
6341 database_ = database;
6342 name_ = [package retain];
6344 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6345 [self addSubview:table_];
6347 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6348 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6350 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6351 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6353 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6354 [subscribedCell_ setShowSelection:NO];
6355 [subscribedCell_ setTitle:@"Show All Changes"];
6356 [subscribedCell_ setControl:subscribedSwitch_];
6358 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6359 [ignoredCell_ setShowSelection:NO];
6360 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6361 [ignoredCell_ setControl:ignoredSwitch_];
6363 [table_ setDataSource:self];
6368 - (void) resetViewAnimated:(BOOL)animated {
6369 [table_ resetViewAnimated:animated];
6372 - (void) reloadData {
6373 if (package_ != nil)
6374 [package_ autorelease];
6375 package_ = [database_ packageWithName:name_];
6376 if (package_ != nil) {
6378 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6379 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6382 [table_ reloadData];
6385 - (NSString *) title {
6391 /* Signature View {{{ */
6392 @interface SignatureView : BrowserView {
6393 _transient Database *database_;
6397 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6401 @implementation SignatureView
6408 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6410 [super webView:sender didClearWindowObject:window forFrame:frame];
6413 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6414 if ((self = [super initWithBook:book]) != nil) {
6415 database_ = database;
6416 package_ = [package retain];
6421 - (void) resetViewAnimated:(BOOL)animated {
6424 - (void) reloadData {
6425 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6431 @interface Cydia : UIApplication <
6432 ConfirmationViewDelegate,
6433 ProgressViewDelegate,
6442 UIToolbar *buttonbar_;
6446 NSMutableArray *essential_;
6447 NSMutableArray *broken_;
6449 Database *database_;
6450 ProgressView *progress_;
6454 UIKeyboard *keyboard_;
6455 UIProgressHUD *hud_;
6457 InstallView *install_;
6458 ChangesView *changes_;
6459 ManageView *manage_;
6460 SearchView *search_;
6465 @implementation Cydia
6468 if ([broken_ count] != 0) {
6469 int count = [broken_ count];
6471 UIActionSheet *sheet = [[[UIActionSheet alloc]
6472 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6473 buttons:[NSArray arrayWithObjects:
6475 @"Ignore (Temporary)",
6477 defaultButtonIndex:0
6482 [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."];
6483 [sheet popupAlertAnimated:YES];
6484 } else if (!Ignored_ && [essential_ count] != 0) {
6485 int count = [essential_ count];
6487 UIActionSheet *sheet = [[[UIActionSheet alloc]
6488 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6489 buttons:[NSArray arrayWithObjects:
6490 @"Upgrade Essential",
6491 @"Complete Upgrade",
6492 @"Ignore (Temporary)",
6494 defaultButtonIndex:0
6499 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6500 [sheet popupAlertAnimated:YES];
6504 - (void) _reloadData {
6505 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6506 [hud setText:@"Reloading Data"];
6507 [overlay_ addSubview:hud];
6510 [database_ reloadData];
6514 [essential_ removeAllObjects];
6515 [broken_ removeAllObjects];
6517 NSArray *packages = [database_ packages];
6518 for (Package *package in packages) {
6520 [broken_ addObject:package];
6521 if ([package upgradableAndEssential:NO]) {
6522 if ([package essential])
6523 [essential_ addObject:package];
6529 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6530 [buttonbar_ setBadgeValue:badge forButton:3];
6531 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6532 [buttonbar_ setBadgeAnimated:YES forButton:3];
6533 [self setApplicationBadge:badge];
6535 [buttonbar_ setBadgeValue:nil forButton:3];
6536 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6537 [buttonbar_ setBadgeAnimated:NO forButton:3];
6538 [self removeApplicationBadge];
6544 if ([packages count] == 0);
6556 [hud removeFromSuperview];*/
6559 - (void) _saveConfig {
6562 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6568 - (void) updateData {
6571 /* XXX: this is just stupid */
6572 if (tag_ != 2 && install_ != nil)
6573 [install_ reloadData];
6574 if (tag_ != 3 && changes_ != nil)
6575 [changes_ reloadData];
6576 if (tag_ != 5 && search_ != nil)
6577 [search_ reloadData];
6587 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6588 _assert(file != NULL);
6590 NSArray *keys = [Sources_ allKeys];
6592 for (int i(0), e([keys count]); i != e; ++i) {
6593 NSString *key = [keys objectAtIndex:i];
6594 NSDictionary *source = [Sources_ objectForKey:key];
6596 fprintf(file, "%s %s %s\n",
6597 [[source objectForKey:@"Type"] UTF8String],
6598 [[source objectForKey:@"URI"] UTF8String],
6599 [[source objectForKey:@"Distribution"] UTF8String]
6608 detachNewThreadSelector:@selector(update_)
6611 title:@"Updating Sources"
6615 - (void) reloadData {
6616 @synchronized (self) {
6617 if (confirm_ == nil)
6623 pkgProblemResolver *resolver = [database_ resolver];
6625 resolver->InstallProtect();
6626 if (!resolver->Resolve(true))
6631 [database_ prepare];
6633 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6634 [confirm_ setDelegate:self];
6636 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6637 [page setDelegate:self];
6639 [confirm_ setPage:page];
6640 [underlay_ popSubview:confirm_];
6643 - (void) installPackage:(Package *)package {
6644 @synchronized (self) {
6651 - (void) removePackage:(Package *)package {
6652 @synchronized (self) {
6659 - (void) distUpgrade {
6660 @synchronized (self) {
6661 [database_ upgrade];
6667 @synchronized (self) {
6669 if (confirm_ != nil) {
6677 [overlay_ removeFromSuperview];
6681 detachNewThreadSelector:@selector(perform)
6688 - (void) bootstrap_ {
6690 [database_ upgrade];
6691 [database_ prepare];
6692 [database_ perform];
6695 - (void) bootstrap {
6697 detachNewThreadSelector:@selector(bootstrap_)
6700 title:@"Bootstrap Install"
6704 - (void) progressViewIsComplete:(ProgressView *)progress {
6705 if (confirm_ != nil) {
6706 [underlay_ addSubview:overlay_];
6707 [confirm_ popFromSuperviewAnimated:NO];
6713 - (void) setPage:(RVPage *)page {
6714 [page resetViewAnimated:NO];
6715 [page setDelegate:self];
6716 [book_ setPage:page];
6719 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6720 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6721 [browser loadURL:url];
6725 - (void) _setHomePage {
6726 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6729 - (void) buttonBarItemTapped:(id)sender {
6730 unsigned tag = [sender tag];
6732 [book_ resetViewAnimated:YES];
6734 } else if (tag_ == 2 && tag != 2)
6735 [install_ resetView];
6738 case 1: [self _setHomePage]; break;
6740 case 2: [self setPage:install_]; break;
6741 case 3: [self setPage:changes_]; break;
6742 case 4: [self setPage:manage_]; break;
6743 case 5: [self setPage:search_]; break;
6745 default: _assert(false);
6751 - (void) applicationWillSuspend {
6753 [super applicationWillSuspend];
6756 - (void) askForSettings {
6757 UIActionSheet *role = [[[UIActionSheet alloc]
6758 initWithTitle:@"Who Are You?"
6759 buttons:[NSArray arrayWithObjects:
6760 @"User (Graphical Only)",
6761 @"Hacker (+ Command Line)",
6762 @"Developer (No Filters)",
6764 defaultButtonIndex:-1
6769 [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."];
6770 [role popupAlertAnimated:YES];
6775 [self setStatusBarShowsProgress:NO];
6778 [hud_ removeFromSuperview];
6782 pid_t pid = ExecFork();
6784 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6785 perror("launchctl stop");
6792 [self askForSettings];
6796 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6798 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6799 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6800 0, 0, screenrect.size.width, screenrect.size.height - 48
6801 ) database:database_];
6803 [book_ setDelegate:self];
6805 [overlay_ addSubview:book_];
6807 NSArray *buttonitems = [NSArray arrayWithObjects:
6808 [NSDictionary dictionaryWithObjectsAndKeys:
6809 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6810 @"home-up.png", kUIButtonBarButtonInfo,
6811 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6812 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6813 self, kUIButtonBarButtonTarget,
6814 @"Home", kUIButtonBarButtonTitle,
6815 @"0", kUIButtonBarButtonType,
6818 [NSDictionary dictionaryWithObjectsAndKeys:
6819 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6820 @"install-up.png", kUIButtonBarButtonInfo,
6821 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6822 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6823 self, kUIButtonBarButtonTarget,
6824 @"Sections", kUIButtonBarButtonTitle,
6825 @"0", kUIButtonBarButtonType,
6828 [NSDictionary dictionaryWithObjectsAndKeys:
6829 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6830 @"changes-up.png", kUIButtonBarButtonInfo,
6831 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6832 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6833 self, kUIButtonBarButtonTarget,
6834 @"Changes", kUIButtonBarButtonTitle,
6835 @"0", kUIButtonBarButtonType,
6838 [NSDictionary dictionaryWithObjectsAndKeys:
6839 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6840 @"manage-up.png", kUIButtonBarButtonInfo,
6841 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6842 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6843 self, kUIButtonBarButtonTarget,
6844 @"Manage", kUIButtonBarButtonTitle,
6845 @"0", kUIButtonBarButtonType,
6848 [NSDictionary dictionaryWithObjectsAndKeys:
6849 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6850 @"search-up.png", kUIButtonBarButtonInfo,
6851 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6852 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6853 self, kUIButtonBarButtonTarget,
6854 @"Search", kUIButtonBarButtonTitle,
6855 @"0", kUIButtonBarButtonType,
6859 buttonbar_ = [[UIToolbar alloc]
6861 withFrame:CGRectMake(
6862 0, screenrect.size.height - ButtonBarHeight_,
6863 screenrect.size.width, ButtonBarHeight_
6865 withItemList:buttonitems
6868 [buttonbar_ setDelegate:self];
6869 [buttonbar_ setBarStyle:1];
6870 [buttonbar_ setButtonBarTrackingMode:2];
6872 int buttons[5] = {1, 2, 3, 4, 5};
6873 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6874 [buttonbar_ showButtonGroup:0 withDuration:0];
6876 for (int i = 0; i != 5; ++i)
6877 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6878 i * 64 + 2, 1, 60, ButtonBarHeight_
6881 [buttonbar_ showSelectionForButton:1];
6882 [overlay_ addSubview:buttonbar_];
6884 [UIKeyboard initImplementationNow];
6885 CGSize keysize = [UIKeyboard defaultSize];
6886 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6887 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6888 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6889 [overlay_ addSubview:keyboard_];
6892 [underlay_ addSubview:overlay_];
6896 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6897 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6898 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6900 manage_ = (ManageView *) [[self
6901 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6902 withClass:[ManageView class]
6908 [self _setHomePage];
6911 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6912 NSString *context = [sheet context];
6913 if ([context isEqualToString:@"fixhalf"])
6916 @synchronized (self) {
6917 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6918 Package *broken = [broken_ objectAtIndex:i];
6921 NSString *id = [broken id];
6922 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6923 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6924 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6925 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6934 [broken_ removeAllObjects];
6941 else if ([context isEqualToString:@"role"]) {
6943 case 1: Role_ = @"User"; break;
6944 case 2: Role_ = @"Hacker"; break;
6945 case 3: Role_ = @"Developer"; break;
6952 bool reset = Settings_ != nil;
6954 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6958 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6966 } else if ([context isEqualToString:@"upgrade"])
6969 @synchronized (self) {
6970 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6971 Package *essential = [essential_ objectAtIndex:i];
6972 [essential install];
6995 - (void) reorganize { _pooled
6996 system("/usr/libexec/cydia/free.sh");
6997 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7000 - (void) applicationSuspend:(__GSEvent *)event {
7001 if (hud_ == nil && ![progress_ isRunning])
7002 [super applicationSuspend:event];
7005 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7007 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7010 - (void) _setSuspended:(BOOL)value {
7012 [super _setSuspended:value];
7015 - (UIProgressHUD *) addProgressHUD {
7016 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7018 [underlay_ addSubview:hud];
7022 - (void) openMailToURL:(NSURL *)url {
7023 // XXX: this makes me sad
7025 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7027 [UIApp openURL:url];
7031 - (RVPage *) pageForPackage:(NSString *)name {
7032 if (Package *package = [database_ packageWithName:name]) {
7033 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7034 [view setPackage:package];
7037 UIActionSheet *sheet = [[[UIActionSheet alloc]
7038 initWithTitle:@"Cannot Locate Package"
7039 buttons:[NSArray arrayWithObjects:@"Close", nil]
7040 defaultButtonIndex:0
7045 [sheet setBodyText:[NSString stringWithFormat:
7046 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7049 [sheet popupAlertAnimated:YES];
7054 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7055 NSString *href = [url absoluteString];
7060 if ([href isEqualToString:@"cydia://add-source"])
7061 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7062 else if ([href isEqualToString:@"cydia://sources"])
7063 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7064 else if ([href isEqualToString:@"cydia://packages"])
7065 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7066 else if ([href hasPrefix:@"cydia://url/"])
7067 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7068 else if ([href hasPrefix:@"cydia://launch/"])
7069 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7070 else if ([href hasPrefix:@"cydia://package-settings/"])
7071 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7072 else if ([href hasPrefix:@"cydia://package-signature/"])
7073 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7074 else if ([href hasPrefix:@"cydia://package/"])
7075 return [self pageForPackage:[href substringFromIndex:16]];
7076 else if ([href hasPrefix:@"cydia://files/"]) {
7077 NSString *name = [href substringFromIndex:14];
7079 if (Package *package = [database_ packageWithName:name]) {
7080 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7081 [files setPackage:package];
7089 - (void) applicationOpenURL:(NSURL *)url {
7090 [super applicationOpenURL:url];
7092 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7093 [self setPage:page];
7094 [buttonbar_ showSelectionForButton:tag];
7099 - (void) applicationDidFinishLaunching:(id)unused {
7100 Font12_ = [[UIFont systemFontOfSize:12] retain];
7101 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7102 Font14_ = [[UIFont systemFontOfSize:14] retain];
7103 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7104 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7106 _assert(pkgInitConfig(*_config));
7107 _assert(pkgInitSystem(*_config, _system));
7111 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7112 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7114 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7116 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7117 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7119 [window_ orderFront:self];
7120 [window_ makeKey:self];
7121 [window_ setHidden:NO];
7123 database_ = [Database sharedInstance];
7124 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7125 [database_ setDelegate:progress_];
7126 [window_ setContentView:progress_];
7128 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7129 [progress_ setContentView:underlay_];
7131 [progress_ resetView];
7134 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7135 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7136 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7137 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7138 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7139 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7140 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7142 [self setIdleTimerDisabled:YES];
7144 hud_ = [self addProgressHUD];
7145 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7147 [self setStatusBarShowsProgress:YES];
7150 detachNewThreadSelector:@selector(reorganize)
7158 /* Web Scripting {{{ */
7159 + (NSString *) webScriptNameForSelector:(SEL)selector {
7160 if (selector == @selector(supports:))
7165 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7166 return selector != @selector(supports:);
7169 - (BOOL) supports:(NSString *)feature {
7170 return [feature isEqualToString:@"window.open"];
7174 - (void) showKeyboard:(BOOL)show {
7175 CGSize keysize = [UIKeyboard defaultSize];
7176 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7177 CGRect keyup = keydown;
7178 keyup.origin.y -= keysize.height;
7180 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7181 [animation setSignificantRectFields:2];
7184 [animation setStartFrame:keydown];
7185 [animation setEndFrame:keyup];
7186 [keyboard_ activate];
7188 [animation setStartFrame:keyup];
7189 [animation setEndFrame:keydown];
7190 [keyboard_ deactivate];
7193 [[UIAnimator sharedAnimator]
7194 addAnimations:[NSArray arrayWithObjects:animation, nil]
7195 withDuration:KeyboardTime_
7200 - (void) slideUp:(UIActionSheet *)alert {
7202 [alert presentSheetFromButtonBar:buttonbar_];
7204 [alert presentSheetInView:overlay_];
7209 void AddPreferences(NSString *plist) { _pooled
7210 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7211 _assert(settings != NULL);
7212 NSMutableArray *items = [settings objectForKey:@"items"];
7216 for (size_t i(0); i != [items count]; ++i) {
7217 NSMutableDictionary *item([items objectAtIndex:i]);
7218 NSString *label = [item objectForKey:@"label"];
7219 if (label != nil && [label isEqualToString:@"Cydia"]) {
7226 for (size_t i(0); i != [items count]; ++i) {
7227 NSDictionary *item([items objectAtIndex:i]);
7228 NSString *label = [item objectForKey:@"label"];
7229 if (label != nil && [label isEqualToString:@"General"]) {
7230 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7231 @"CydiaSettings", @"bundle",
7232 @"PSLinkCell", @"cell",
7233 [NSNumber numberWithBool:YES], @"hasIcon",
7234 [NSNumber numberWithBool:YES], @"isController",
7236 nil] atIndex:(i + 1)];
7242 _assert([settings writeToFile:plist atomically:YES] == YES);
7247 id Alloc_(id self, SEL selector) {
7248 id object = alloc_(self, selector);
7249 lprintf("[%s]A-%p\n", self->isa->name, object);
7254 id Dealloc_(id self, SEL selector) {
7255 id object = dealloc_(self, selector);
7256 lprintf("[%s]D-%p\n", self->isa->name, object);
7260 int main(int argc, char *argv[]) { _pooled
7261 bool substrate(false);
7267 for (int argi(1); argi != argc; ++argi)
7268 if (strcmp(argv[argi], "--") == 0) {
7270 argv[argi] = argv[0];
7276 for (int argi(1); argi != arge; ++argi)
7277 if (strcmp(args[argi], "--bootstrap") == 0)
7279 else if (strcmp(args[argi], "--substrate") == 0)
7282 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7285 App_ = [[NSBundle mainBundle] bundlePath];
7286 Home_ = NSHomeDirectory();
7287 Locale_ = CFLocaleCopyCurrent();
7290 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7291 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7292 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7293 Sounds_Keyboard_ = [keyboard boolValue];
7299 #if 1 /* XXX: this costs 1.4s of startup performance */
7300 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7301 _assert(errno == ENOENT);
7302 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7303 _assert(errno == ENOENT);
7306 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7307 alloc_ = alloc->method_imp;
7308 alloc->method_imp = (IMP) &Alloc_;*/
7310 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7311 dealloc_ = dealloc->method_imp;
7312 dealloc->method_imp = (IMP) &Dealloc_;*/
7317 size = sizeof(maxproc);
7318 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7319 perror("sysctlbyname(\"kern.maxproc\", ?)");
7320 else if (maxproc < 64) {
7322 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7323 perror("sysctlbyname(\"kern.maxproc\", #)");
7326 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7327 char *machine = new char[size];
7328 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7329 perror("sysctlbyname(\"hw.machine\", ?)");
7333 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7335 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7336 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7338 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7339 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7341 Settings_ = [Metadata_ objectForKey:@"Settings"];
7343 Packages_ = [Metadata_ objectForKey:@"Packages"];
7344 Sections_ = [Metadata_ objectForKey:@"Sections"];
7345 Sources_ = [Metadata_ objectForKey:@"Sources"];
7348 if (Settings_ != nil)
7349 Role_ = [Settings_ objectForKey:@"Role"];
7351 if (Packages_ == nil) {
7352 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7353 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7356 if (Sections_ == nil) {
7357 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7358 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7361 if (Sources_ == nil) {
7362 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7363 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7367 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7370 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7371 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7373 if (access("/User", F_OK) != 0)
7374 system("/usr/libexec/cydia/firmware.sh");
7376 _assert([[NSFileManager defaultManager]
7377 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7378 withIntermediateDirectories:YES
7383 space_ = CGColorSpaceCreateDeviceRGB();
7385 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7386 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7387 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7388 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7389 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7390 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7392 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7394 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7396 UIApplicationUseLegacyEvents(YES);
7397 UIKeyboardDisableAutomaticAppearance();
7399 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7401 CGColorSpaceRelease(space_);