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 #include <objc/objc.h>
40 #include <objc/runtime.h>
42 #include <CoreGraphics/CoreGraphics.h>
43 #include <GraphicsServices/GraphicsServices.h>
44 #include <Foundation/Foundation.h>
46 #import <QuartzCore/CALayer.h>
48 #import <UIKit/UIKit.h>
51 #import <MessageUI/MailComposeController.h>
53 #import <WebCore/WebScriptObject.h>
54 //#include <WebCore/DOMHTML.h>
56 #include <WebKit/WebFrame.h>
57 #include <WebKit/WebPolicyDelegate.h>
58 #include <WebKit/WebView.h>
60 #import <WebKit/WebView-WebPrivate.h>
65 #include <ext/stdio_filebuf.h>
67 #include <apt-pkg/acquire.h>
68 #include <apt-pkg/acquire-item.h>
69 #include <apt-pkg/algorithms.h>
70 #include <apt-pkg/cachefile.h>
71 #include <apt-pkg/clean.h>
72 #include <apt-pkg/configuration.h>
73 #include <apt-pkg/debmetaindex.h>
74 #include <apt-pkg/error.h>
75 #include <apt-pkg/init.h>
76 #include <apt-pkg/mmap.h>
77 #include <apt-pkg/pkgrecords.h>
78 #include <apt-pkg/sha1.h>
79 #include <apt-pkg/sourcelist.h>
80 #include <apt-pkg/sptr.h>
81 #include <apt-pkg/strutl.h>
83 #include <sys/types.h>
85 #include <sys/sysctl.h>
91 #include <mach-o/nlist.h>
101 #import "BrowserView.h"
102 #import "ResetView.h"
103 #import "UICaboodle.h"
106 //#define _finline __attribute__((force_inline))
107 #define _finline inline
109 /* Objective-C Handle<> {{{ */
110 template <typename Type_>
112 typedef _H<Type_> This_;
117 _finline void Retain_() {
122 _finline void Clear_() {
128 _finline _H(Type_ *value = NULL, bool mended = false) :
139 _finline This_ &operator =(Type_ *value) {
140 if (value_ != value) {
149 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
151 void NSLogRect(const char *fix, const CGRect &rect) {
152 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
155 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
157 /* iPhoneOS 2.0 Compatibility {{{ */
159 @interface UITextView (iPhoneOS)
160 - (void) setTextSize:(float)size;
163 @implementation UITextView (iPhoneOS)
165 - (void) setTextSize:(float)size {
166 [self setFont:[[self font] fontWithSize:size]];
173 extern NSString * const kCAFilterNearest;
175 /* Information Dictionaries {{{ */
176 @interface NSMutableArray (Cydia)
177 - (void) addInfoDictionary:(NSDictionary *)info;
180 @implementation NSMutableArray (Cydia)
182 - (void) addInfoDictionary:(NSDictionary *)info {
183 [self addObject:info];
188 @interface NSMutableDictionary (Cydia)
189 - (void) addInfoDictionary:(NSDictionary *)info;
192 @implementation NSMutableDictionary (Cydia)
194 - (void) addInfoDictionary:(NSDictionary *)info {
195 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
196 [self setObject:info forKey:bundle];
201 /* Pop Transitions {{{ */
202 @interface PopTransitionView : UITransitionView {
207 @implementation PopTransitionView
209 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
210 if (from != nil && to == nil)
211 [self removeFromSuperview];
216 @interface UIView (PopUpView)
217 - (void) popFromSuperviewAnimated:(BOOL)animated;
218 - (void) popSubview:(UIView *)view;
221 @implementation UIView (PopUpView)
223 - (void) popFromSuperviewAnimated:(BOOL)animated {
224 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
227 - (void) popSubview:(UIView *)view {
228 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
229 [transition setDelegate:transition];
230 [self addSubview:transition];
232 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
233 [transition transition:UITransitionNone toView:blank];
234 [transition transition:UITransitionPushFromBottom toView:view];
240 #define lprintf(args...) fprintf(stderr, args)
243 #define RecycleWebViews 0
246 kUIControlEventMouseDown = 1 << 0,
247 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
248 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
249 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
250 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
251 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
252 } UIControlEventMasks;
254 @interface NSString (UIKit)
255 - (NSString *) stringByAddingPercentEscapes;
256 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
259 @interface NSString (Cydia)
260 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
261 - (NSComparisonResult) compareByPath:(NSString *)other;
264 @implementation NSString (Cydia)
266 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
267 char data[length + 1];
268 memcpy(data, bytes, length);
270 return [NSString stringWithUTF8String:data];
273 - (NSComparisonResult) compareByPath:(NSString *)other {
274 NSString *prefix = [self commonPrefixWithString:other options:0];
275 size_t length = [prefix length];
277 NSRange lrange = NSMakeRange(length, [self length] - length);
278 NSRange rrange = NSMakeRange(length, [other length] - length);
280 lrange = [self rangeOfString:@"/" options:0 range:lrange];
281 rrange = [other rangeOfString:@"/" options:0 range:rrange];
283 NSComparisonResult value;
285 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
286 value = NSOrderedSame;
287 else if (lrange.location == NSNotFound)
288 value = NSOrderedAscending;
289 else if (rrange.location == NSNotFound)
290 value = NSOrderedDescending;
292 value = NSOrderedSame;
294 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
295 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
296 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
297 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
299 NSComparisonResult result = [lpath compare:rpath];
300 return result == NSOrderedSame ? value : result;
305 /* Perl-Compatible RegEx {{{ */
315 Pcre(const char *regex) :
320 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
323 lprintf("%d:%s\n", offset, error);
327 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
328 matches_ = new int[(capture_ + 1) * 3];
336 NSString *operator [](size_t match) {
337 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
340 bool operator ()(NSString *data) {
341 // XXX: length is for characters, not for bytes
342 return operator ()([data UTF8String], [data length]);
345 bool operator ()(const char *data, size_t size) {
347 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
351 /* Mime Addresses {{{ */
352 @interface Address : NSObject {
358 - (NSString *) address;
360 + (Address *) addressWithString:(NSString *)string;
361 - (Address *) initWithString:(NSString *)string;
364 @implementation Address
373 - (NSString *) name {
377 - (NSString *) address {
381 + (Address *) addressWithString:(NSString *)string {
382 return [[[Address alloc] initWithString:string] autorelease];
385 + (NSArray *) _attributeKeys {
386 return [NSArray arrayWithObjects:@"address", @"name", nil];
389 - (NSArray *) attributeKeys {
390 return [[self class] _attributeKeys];
393 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
394 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
397 - (Address *) initWithString:(NSString *)string {
398 if ((self = [super init]) != nil) {
399 const char *data = [string UTF8String];
400 size_t size = [string length];
402 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
404 if (address_r(data, size)) {
405 name_ = [address_r[1] retain];
406 address_ = [address_r[2] retain];
408 name_ = [string retain];
416 /* CoreGraphics Primitives {{{ */
427 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
430 Set(space, red, green, blue, alpha);
435 CGColorRelease(color_);
442 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
444 float color[] = {red, green, blue, alpha};
445 color_ = CGColorCreate(space, color);
448 operator CGColorRef() {
454 extern "C" void UISetColor(CGColorRef color);
456 /* Random Global Variables {{{ */
457 static const int PulseInterval_ = 50000;
458 static const int ButtonBarHeight_ = 48;
459 static const float KeyboardTime_ = 0.3f;
461 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
462 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
464 static CGColor Blue_;
465 static CGColor Blueish_;
466 static CGColor Black_;
468 static CGColor White_;
469 static CGColor Gray_;
471 static NSString *App_;
472 static NSString *Home_;
473 static BOOL Sounds_Keyboard_;
475 static BOOL Advanced_;
479 static BOOL Ignored_;
481 static UIFont *Font12_;
482 static UIFont *Font12Bold_;
483 static UIFont *Font14_;
484 static UIFont *Font18Bold_;
485 static UIFont *Font22Bold_;
487 static const char *Machine_ = NULL;
488 static const NSString *UniqueID_ = NULL;
495 CGColorSpaceRef space_;
497 #define FW_LEAST(major, minor, bugfix) \
498 (major < Major_ || major == Major_ && \
499 (minor < Minor_ || minor == Minor_ && \
505 static NSDictionary *SectionMap_;
506 static NSMutableDictionary *Metadata_;
507 static _transient NSMutableDictionary *Settings_;
508 static _transient NSString *Role_;
509 static _transient NSMutableDictionary *Packages_;
510 static _transient NSMutableDictionary *Sections_;
511 static _transient NSMutableDictionary *Sources_;
512 static bool Changed_;
516 static NSMutableArray *Documents_;
519 NSString *GetLastUpdate() {
520 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
523 return @"Never or Unknown";
525 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
526 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
528 CFRelease(formatter);
530 return [(NSString *) formatted autorelease];
533 /* Display Helpers {{{ */
534 inline float Interpolate(float begin, float end, float fraction) {
535 return (end - begin) * fraction + begin;
538 NSString *SizeString(double size) {
539 bool negative = size < 0;
544 while (size > 1024) {
549 static const char *powers_[] = {"B", "kB", "MB", "GB"};
551 return [NSString stringWithFormat:@"%s%.1f%s", (negative ? "-" : ""), size, powers_[power]];
554 NSString *StripVersion(NSString *version) {
555 NSRange colon = [version rangeOfString:@":"];
556 if (colon.location != NSNotFound)
557 version = [version substringFromIndex:(colon.location + 1)];
561 NSString *Simplify(NSString *title) {
562 const char *data = [title UTF8String];
563 size_t size = [title length];
565 static Pcre square_r("^\\[(.*)\\]$");
566 if (square_r(data, size))
567 return Simplify(square_r[1]);
569 static Pcre paren_r("^\\((.*)\\)$");
570 if (paren_r(data, size))
571 return Simplify(paren_r[1]);
573 static Pcre title_r("^(.*?) \\(.*\\)$");
574 if (title_r(data, size))
575 return Simplify(title_r[1]);
581 bool isSectionVisible(NSString *section) {
582 NSDictionary *metadata = [Sections_ objectForKey:section];
583 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
584 return hidden == nil || ![hidden boolValue];
587 /* Delegate Prototypes {{{ */
591 @interface NSObject (ProgressDelegate)
594 @implementation NSObject(ProgressDelegate)
596 - (void) _setProgressError:(NSArray *)args {
597 [self performSelector:@selector(setProgressError:forPackage:)
598 withObject:[args objectAtIndex:0]
599 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
605 @protocol ProgressDelegate
606 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
607 - (void) setProgressTitle:(NSString *)title;
608 - (void) setProgressPercent:(float)percent;
609 - (void) startProgress;
610 - (void) addProgressOutput:(NSString *)output;
611 - (bool) isCancelling:(size_t)received;
614 @protocol ConfigurationDelegate
615 - (void) repairWithSelector:(SEL)selector;
616 - (void) setConfigurationData:(NSString *)data;
619 @protocol CydiaDelegate
620 - (void) installPackage:(Package *)package;
621 - (void) removePackage:(Package *)package;
622 - (void) slideUp:(UIActionSheet *)alert;
623 - (void) distUpgrade;
626 - (void) askForSettings;
627 - (UIProgressHUD *) addProgressHUD;
628 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
629 - (RVPage *) pageForPackage:(NSString *)name;
630 - (void) openMailToURL:(NSURL *)url;
634 /* Status Delegation {{{ */
636 public pkgAcquireStatus
639 _transient NSObject<ProgressDelegate> *delegate_;
647 void setDelegate(id delegate) {
648 delegate_ = delegate;
651 virtual bool MediaChange(std::string media, std::string drive) {
655 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
658 virtual void Fetch(pkgAcquire::ItemDesc &item) {
659 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
662 virtual void Done(pkgAcquire::ItemDesc &item) {
665 virtual void Fail(pkgAcquire::ItemDesc &item) {
667 item.Owner->Status == pkgAcquire::Item::StatIdle ||
668 item.Owner->Status == pkgAcquire::Item::StatDone
672 std::string &error(item.Owner->ErrorText);
676 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
677 NSArray *fields([description componentsSeparatedByString:@" "]);
678 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
680 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
681 withObject:[NSArray arrayWithObjects:
682 [NSString stringWithUTF8String:error.c_str()],
689 virtual bool Pulse(pkgAcquire *Owner) {
690 bool value = pkgAcquireStatus::Pulse(Owner);
693 double(CurrentBytes + CurrentItems) /
694 double(TotalBytes + TotalItems)
697 [delegate_ setProgressPercent:percent];
698 return [delegate_ isCancelling:CurrentBytes] ? false : value;
701 virtual void Start() {
702 [delegate_ startProgress];
705 virtual void Stop() {
709 /* Progress Delegation {{{ */
714 _transient id<ProgressDelegate> delegate_;
717 virtual void Update() {
718 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
719 [delegate_ setProgressPercent:(Percent / 100)];
728 void setDelegate(id delegate) {
729 delegate_ = delegate;
732 virtual void Done() {
733 [delegate_ setProgressPercent:1];
738 /* Database Interface {{{ */
739 @interface Database : NSObject {
741 pkgDepCache::Policy *policy_;
742 pkgRecords *records_;
743 pkgProblemResolver *resolver_;
744 pkgAcquire *fetcher_;
746 SPtr<pkgPackageManager> manager_;
747 pkgSourceList *list_;
749 NSMutableDictionary *sources_;
750 NSMutableArray *packages_;
752 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
761 + (Database *) sharedInstance;
763 - (void) _readCydia:(NSNumber *)fd;
764 - (void) _readStatus:(NSNumber *)fd;
765 - (void) _readOutput:(NSNumber *)fd;
769 - (Package *) packageWithName:(NSString *)name;
771 - (pkgCacheFile &) cache;
772 - (pkgDepCache::Policy *) policy;
773 - (pkgRecords *) records;
774 - (pkgProblemResolver *) resolver;
775 - (pkgAcquire &) fetcher;
776 - (NSArray *) packages;
777 - (NSArray *) sources;
786 - (void) updateWithStatus:(Status &)status;
788 - (void) setDelegate:(id)delegate;
789 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
793 /* Source Class {{{ */
794 @interface Source : NSObject {
795 NSString *description_;
800 NSString *distribution_;
804 NSString *defaultIcon_;
806 NSDictionary *record_;
810 - (Source *) initWithMetaIndex:(metaIndex *)index;
812 - (NSComparisonResult) compareByNameAndType:(Source *)source;
814 - (NSDictionary *) record;
818 - (NSString *) distribution;
824 - (NSString *) description;
825 - (NSString *) label;
826 - (NSString *) origin;
827 - (NSString *) version;
829 - (NSString *) defaultIcon;
833 @implementation Source
837 [distribution_ release];
840 if (description_ != nil)
841 [description_ release];
848 if (defaultIcon_ != nil)
849 [defaultIcon_ release];
856 + (NSArray *) _attributeKeys {
857 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
860 - (NSArray *) attributeKeys {
861 return [[self class] _attributeKeys];
864 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
865 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
868 - (Source *) initWithMetaIndex:(metaIndex *)index {
869 if ((self = [super init]) != nil) {
870 trusted_ = index->IsTrusted();
872 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
873 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
874 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
876 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
877 if (dindex != NULL) {
878 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
880 while (std::getline(release, line)) {
881 std::string::size_type colon(line.find(':'));
882 if (colon == std::string::npos)
885 std::string name(line.substr(0, colon));
886 std::string value(line.substr(colon + 1));
887 while (!value.empty() && value[0] == ' ')
888 value = value.substr(1);
890 if (name == "Default-Icon")
891 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
892 else if (name == "Description")
893 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
894 else if (name == "Label")
895 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
896 else if (name == "Origin")
897 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
898 else if (name == "Version")
899 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
903 record_ = [Sources_ objectForKey:[self key]];
905 record_ = [record_ retain];
909 - (NSComparisonResult) compareByNameAndType:(Source *)source {
910 NSDictionary *lhr = [self record];
911 NSDictionary *rhr = [source record];
914 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
916 NSString *lhs = [self name];
917 NSString *rhs = [source name];
919 if ([lhs length] != 0 && [rhs length] != 0) {
920 unichar lhc = [lhs characterAtIndex:0];
921 unichar rhc = [rhs characterAtIndex:0];
923 if (isalpha(lhc) && !isalpha(rhc))
924 return NSOrderedAscending;
925 else if (!isalpha(lhc) && isalpha(rhc))
926 return NSOrderedDescending;
929 return [lhs compare:rhs options:CompareOptions_];
932 - (NSDictionary *) record {
944 - (NSString *) distribution {
945 return distribution_;
948 - (NSString *) type {
953 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
956 - (NSString *) host {
957 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
960 - (NSString *) name {
961 return origin_ == nil ? [self host] : origin_;
964 - (NSString *) description {
968 - (NSString *) label {
969 return label_ == nil ? [self host] : label_;
972 - (NSString *) origin {
976 - (NSString *) version {
980 - (NSString *) defaultIcon {
986 /* Relationship Class {{{ */
987 @interface Relationship : NSObject {
998 @implementation Relationship
1006 - (NSString *) type {
1014 - (NSString *) name {
1021 /* Package Class {{{ */
1022 NSString *Scour(const char *field, const char *begin, const char *end) {
1023 size_t i(0), l(strlen(field));
1026 const char *name = begin + i;
1027 const char *colon = name + l;
1028 const char *value = colon + 1;
1033 strncasecmp(name, field, l) == 0
1035 while (value != end && value[0] == ' ')
1037 const char *line = std::find(value, end, '\n');
1038 while (line != value && line[-1] == ' ')
1041 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1043 begin = std::find(begin, end, '\n');
1051 @interface Package : NSObject {
1052 pkgCache::PkgIterator iterator_;
1053 _transient Database *database_;
1054 pkgCache::VerIterator version_;
1055 pkgCache::VerFileIterator file_;
1061 NSString *installed_;
1067 NSString *depiction_;
1068 NSString *homepage_;
1074 NSArray *relationships_;
1077 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1078 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1080 - (pkgCache::PkgIterator) iterator;
1082 - (NSString *) section;
1083 - (Address *) maintainer;
1085 - (NSString *) description;
1086 - (NSString *) index;
1090 - (NSString *) latest;
1091 - (NSString *) installed;
1094 - (BOOL) upgradableAndEssential:(BOOL)essential;
1097 - (BOOL) unfiltered;
1101 - (BOOL) halfConfigured;
1102 - (BOOL) halfInstalled;
1104 - (NSString *) mode;
1107 - (NSString *) name;
1108 - (NSString *) tagline;
1110 - (NSString *) homepage;
1111 - (NSString *) depiction;
1112 - (Address *) author;
1114 - (NSArray *) files;
1115 - (NSArray *) relationships;
1116 - (NSArray *) warnings;
1117 - (NSArray *) applications;
1119 - (Source *) source;
1120 - (NSString *) role;
1122 - (BOOL) matches:(NSString *)text;
1124 - (bool) hasSupportingRole;
1125 - (BOOL) hasTag:(NSString *)tag;
1126 - (NSString *) primaryPurpose;
1127 - (NSArray *) purposes;
1129 - (NSComparisonResult) compareByName:(Package *)package;
1130 - (NSComparisonResult) compareBySection:(Package *)package;
1131 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1132 - (NSComparisonResult) compareForChanges:(Package *)package;
1137 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1138 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1139 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1140 - (NSNumber *) isVisibleInSource:(Source *)source;
1144 @implementation Package
1151 if (installed_ != nil)
1152 [installed_ release];
1160 if (depiction_ != nil)
1161 [depiction_ release];
1162 if (homepage_ != nil)
1163 [homepage_ release];
1164 if (sponsor_ != nil)
1173 if (relationships_ != nil)
1174 [relationships_ release];
1179 + (NSArray *) _attributeKeys {
1180 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1183 - (NSArray *) attributeKeys {
1184 return [[self class] _attributeKeys];
1187 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1188 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1191 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1192 if ((self = [super init]) != nil) {
1193 iterator_ = iterator;
1194 database_ = database;
1196 version_ = [database_ policy]->GetCandidateVer(iterator_);
1197 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1199 if (!version_.end())
1200 file_ = version_.FileList();
1202 pkgCache &cache([database_ cache]);
1203 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1206 pkgCache::VerIterator current = iterator_.CurrentVer();
1207 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1209 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1212 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1214 const char *begin, *end;
1215 parser->GetRec(begin, end);
1217 name_ = Scour("name", begin, end);
1219 name_ = [name_ retain];
1220 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1221 icon_ = Scour("icon", begin, end);
1223 icon_ = [icon_ retain];
1224 depiction_ = Scour("depiction", begin, end);
1225 if (depiction_ != nil)
1226 depiction_ = [depiction_ retain];
1227 homepage_ = Scour("homepage", begin, end);
1228 if (homepage_ == nil)
1229 homepage_ = Scour("website", begin, end);
1230 if ([homepage_ isEqualToString:depiction_])
1232 if (homepage_ != nil)
1233 homepage_ = [homepage_ retain];
1234 NSString *sponsor = Scour("sponsor", begin, end);
1236 sponsor_ = [[Address addressWithString:sponsor] retain];
1237 NSString *author = Scour("author", begin, end);
1239 author_ = [[Address addressWithString:author] retain];
1240 NSString *tags = Scour("tag", begin, end);
1242 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1246 for (int i(0), e([tags_ count]); i != e; ++i) {
1247 NSString *tag = [tags_ objectAtIndex:i];
1248 if ([tag hasPrefix:@"role::"]) {
1249 role_ = [[tag substringFromIndex:6] retain];
1254 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1255 if (metadata == nil || [metadata count] == 0) {
1256 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1260 [Packages_ setObject:metadata forKey:id_];
1266 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1267 return [[[Package alloc]
1268 initWithIterator:iterator
1273 - (pkgCache::PkgIterator) iterator {
1277 - (NSString *) section {
1278 const char *section = iterator_.Section();
1279 if (section == NULL)
1282 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1285 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1286 if (NSString *rename = [value objectForKey:@"Rename"]) {
1291 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1294 - (Address *) maintainer {
1297 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1298 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1302 return version_.end() ? 0 : version_->InstalledSize;
1305 - (NSString *) description {
1308 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1309 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1311 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1312 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1313 if ([lines count] < 2)
1316 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1317 for (size_t i(1); i != [lines count]; ++i) {
1318 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1319 [trimmed addObject:trim];
1322 return [trimmed componentsJoinedByString:@"\n"];
1325 - (NSString *) index {
1326 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1327 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1331 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1334 - (NSString *) latest {
1338 - (NSString *) installed {
1343 return !version_.end();
1346 - (BOOL) upgradableAndEssential:(BOOL)essential {
1347 pkgCache::VerIterator current = iterator_.CurrentVer();
1350 return essential && [self essential];
1352 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1353 return !candidate.end() && candidate != current;
1357 - (BOOL) essential {
1358 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1362 return [database_ cache][iterator_].InstBroken();
1365 - (BOOL) unfiltered {
1366 NSString *section = [self section];
1367 return section == nil || isSectionVisible(section);
1371 return [self hasSupportingRole] && [self unfiltered];
1375 unsigned char current = iterator_->CurrentState;
1376 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1379 - (BOOL) halfConfigured {
1380 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1383 - (BOOL) halfInstalled {
1384 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1388 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1389 return state.Mode != pkgDepCache::ModeKeep;
1392 - (NSString *) mode {
1393 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1395 switch (state.Mode) {
1396 case pkgDepCache::ModeDelete:
1397 if ((state.iFlags & pkgDepCache::Purge) != 0)
1401 case pkgDepCache::ModeKeep:
1402 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1406 case pkgDepCache::ModeInstall:
1407 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1408 return @"Reinstall";
1409 else switch (state.Status) {
1411 return @"Downgrade";
1417 return @"New Install";
1430 - (NSString *) name {
1431 return name_ == nil ? id_ : name_;
1434 - (NSString *) tagline {
1438 - (UIImage *) icon {
1439 NSString *section = [self section];
1441 section = Simplify(section);
1444 if (NSString *icon = icon_)
1445 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1446 if (icon == nil) if (section != nil)
1447 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1448 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1449 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1451 icon = [UIImage applicationImageNamed:@"unknown.png"];
1455 - (NSString *) homepage {
1459 - (NSString *) depiction {
1463 - (Address *) sponsor {
1467 - (Address *) author {
1471 - (NSArray *) files {
1472 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1473 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1476 fin.open([path UTF8String]);
1481 while (std::getline(fin, line))
1482 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1487 - (NSArray *) relationships {
1488 return relationships_;
1491 - (NSArray *) warnings {
1492 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1493 const char *name(iterator_.Name());
1495 size_t length(strlen(name));
1496 if (length < 2) invalid:
1497 [warnings addObject:@"illegal package identifier"];
1498 else for (size_t i(0); i != length; ++i)
1500 (name[i] < 'a' || name[i] > 'z') &&
1501 (name[i] < '0' || name[i] > '9') &&
1502 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1505 if (strcmp(name, "cydia") != 0) {
1509 if (NSArray *files = [self files])
1510 for (NSString *file in files)
1511 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1513 else if (!stash && [file isEqualToString:@"/var/stash"])
1517 [warnings addObject:@"files installed into Cydia.app"];
1519 [warnings addObject:@"files installed to /var/stash"];
1522 return [warnings count] == 0 ? nil : warnings;
1525 - (NSArray *) applications {
1526 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1528 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1530 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1531 if (NSArray *files = [self files])
1532 for (NSString *file in files)
1533 if (application_r(file)) {
1534 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1535 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1536 if ([id isEqualToString:me])
1539 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1541 display = application_r[1];
1543 NSString *bundle([file stringByDeletingLastPathComponent]);
1544 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1545 if (icon == nil || [icon length] == 0)
1547 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1549 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1550 [applications addObject:application];
1552 [application addObject:id];
1553 [application addObject:display];
1554 [application addObject:url];
1557 return [applications count] == 0 ? nil : applications;
1560 - (Source *) source {
1562 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1569 - (NSString *) role {
1573 - (BOOL) matches:(NSString *)text {
1579 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1580 if (range.location != NSNotFound)
1583 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1584 if (range.location != NSNotFound)
1587 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1588 if (range.location != NSNotFound)
1594 - (bool) hasSupportingRole {
1597 if ([role_ isEqualToString:@"enduser"])
1599 if ([Role_ isEqualToString:@"User"])
1601 if ([role_ isEqualToString:@"hacker"])
1603 if ([Role_ isEqualToString:@"Hacker"])
1605 if ([role_ isEqualToString:@"developer"])
1607 if ([Role_ isEqualToString:@"Developer"])
1612 - (BOOL) hasTag:(NSString *)tag {
1613 return tags_ == nil ? NO : [tags_ containsObject:tag];
1616 - (NSString *) primaryPurpose {
1617 for (NSString *tag in tags_)
1618 if ([tag hasPrefix:@"purpose::"])
1619 return [tag substringFromIndex:9];
1623 - (NSArray *) purposes {
1624 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1625 for (NSString *tag in tags_)
1626 if ([tag hasPrefix:@"purpose::"])
1627 [purposes addObject:[tag substringFromIndex:9]];
1628 return [purposes count] == 0 ? nil : purposes;
1631 - (NSComparisonResult) compareByName:(Package *)package {
1632 NSString *lhs = [self name];
1633 NSString *rhs = [package name];
1635 if ([lhs length] != 0 && [rhs length] != 0) {
1636 unichar lhc = [lhs characterAtIndex:0];
1637 unichar rhc = [rhs characterAtIndex:0];
1639 if (isalpha(lhc) && !isalpha(rhc))
1640 return NSOrderedAscending;
1641 else if (!isalpha(lhc) && isalpha(rhc))
1642 return NSOrderedDescending;
1645 return [lhs compare:rhs options:CompareOptions_];
1648 - (NSComparisonResult) compareBySection:(Package *)package {
1649 NSString *lhs = [self section];
1650 NSString *rhs = [package section];
1652 if (lhs == NULL && rhs != NULL)
1653 return NSOrderedAscending;
1654 else if (lhs != NULL && rhs == NULL)
1655 return NSOrderedDescending;
1656 else if (lhs != NULL && rhs != NULL) {
1657 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1658 if (result != NSOrderedSame)
1662 return NSOrderedSame;
1665 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1666 NSString *lhs = [self section];
1667 NSString *rhs = [package section];
1669 if (lhs == NULL && rhs != NULL)
1670 return NSOrderedAscending;
1671 else if (lhs != NULL && rhs == NULL)
1672 return NSOrderedDescending;
1673 else if (lhs != NULL && rhs != NULL) {
1674 NSComparisonResult result = [lhs compare:rhs];
1675 if (result != NSOrderedSame)
1679 return [self compareByName:package];
1682 - (NSComparisonResult) compareForChanges:(Package *)package {
1683 BOOL lhs = [self upgradableAndEssential:YES];
1684 BOOL rhs = [package upgradableAndEssential:YES];
1687 return lhs ? NSOrderedAscending : NSOrderedDescending;
1689 switch ([[self seen] compare:[package seen]]) {
1690 case NSOrderedAscending:
1691 return NSOrderedDescending;
1694 case NSOrderedDescending:
1695 return NSOrderedAscending;
1701 return [self compareByName:package];
1705 pkgProblemResolver *resolver = [database_ resolver];
1706 resolver->Clear(iterator_);
1707 resolver->Protect(iterator_);
1708 pkgCacheFile &cache([database_ cache]);
1709 cache->MarkInstall(iterator_, false);
1710 pkgDepCache::StateCache &state((*cache)[iterator_]);
1711 if (!state.Install())
1712 cache->SetReInstall(iterator_, true);
1716 pkgProblemResolver *resolver = [database_ resolver];
1717 resolver->Clear(iterator_);
1718 resolver->Protect(iterator_);
1719 resolver->Remove(iterator_);
1720 [database_ cache]->MarkDelete(iterator_, true);
1723 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1724 return [NSNumber numberWithBool:(
1725 [self unfiltered] && [self matches:search]
1729 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1730 return [NSNumber numberWithBool:(
1731 (![number boolValue] || [self visible]) && [self installed] != nil
1735 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1736 NSString *section = [self section];
1738 return [NSNumber numberWithBool:(
1740 [self installed] == nil && (
1742 section == nil && [name length] == 0 ||
1743 [name isEqualToString:section]
1748 - (NSNumber *) isVisibleInSource:(Source *)source {
1749 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1754 /* Section Class {{{ */
1755 @interface Section : NSObject {
1761 - (NSComparisonResult) compareByName:(Section *)section;
1762 - (Section *) initWithName:(NSString *)name;
1763 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1764 - (NSString *) name;
1767 - (void) addToCount;
1771 @implementation Section
1778 - (NSComparisonResult) compareByName:(Section *)section {
1779 NSString *lhs = [self name];
1780 NSString *rhs = [section name];
1782 if ([lhs length] != 0 && [rhs length] != 0) {
1783 unichar lhc = [lhs characterAtIndex:0];
1784 unichar rhc = [rhs characterAtIndex:0];
1786 if (isalpha(lhc) && !isalpha(rhc))
1787 return NSOrderedAscending;
1788 else if (!isalpha(lhc) && isalpha(rhc))
1789 return NSOrderedDescending;
1792 return [lhs compare:rhs options:CompareOptions_];
1795 - (Section *) initWithName:(NSString *)name {
1796 return [self initWithName:name row:0];
1799 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1800 if ((self = [super init]) != nil) {
1801 name_ = [name retain];
1806 - (NSString *) name {
1818 - (void) addToCount {
1826 static NSArray *Finishes_;
1828 /* Database Implementation {{{ */
1829 @implementation Database
1831 + (Database *) sharedInstance {
1832 static Database *instance;
1833 if (instance == nil)
1834 instance = [[Database alloc] init];
1843 - (void) _readCydia:(NSNumber *)fd { _pooled
1844 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1845 std::istream is(&ib);
1848 static Pcre finish_r("^finish:([^:]*)$");
1850 while (std::getline(is, line)) {
1851 const char *data(line.c_str());
1852 size_t size = line.size();
1853 lprintf("C:%s\n", data);
1855 if (finish_r(data, size)) {
1856 NSString *finish = finish_r[1];
1857 int index = [Finishes_ indexOfObject:finish];
1858 if (index != INT_MAX && index > Finish_)
1866 - (void) _readStatus:(NSNumber *)fd { _pooled
1867 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1868 std::istream is(&ib);
1871 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1872 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1874 while (std::getline(is, line)) {
1875 const char *data(line.c_str());
1876 size_t size = line.size();
1877 lprintf("S:%s\n", data);
1879 if (conffile_r(data, size)) {
1880 [delegate_ setConfigurationData:conffile_r[1]];
1881 } else if (strncmp(data, "status: ", 8) == 0) {
1882 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1883 [delegate_ setProgressTitle:string];
1884 } else if (pmstatus_r(data, size)) {
1885 std::string type([pmstatus_r[1] UTF8String]);
1886 NSString *id = pmstatus_r[2];
1888 float percent([pmstatus_r[3] floatValue]);
1889 [delegate_ setProgressPercent:(percent / 100)];
1891 NSString *string = pmstatus_r[4];
1893 if (type == "pmerror")
1894 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1895 withObject:[NSArray arrayWithObjects:string, id, nil]
1898 else if (type == "pmstatus")
1899 [delegate_ setProgressTitle:string];
1900 else if (type == "pmconffile")
1901 [delegate_ setConfigurationData:string];
1902 else _assert(false);
1903 } else _assert(false);
1909 - (void) _readOutput:(NSNumber *)fd { _pooled
1910 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1911 std::istream is(&ib);
1914 while (std::getline(is, line)) {
1915 lprintf("O:%s\n", line.c_str());
1916 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1926 - (Package *) packageWithName:(NSString *)name {
1927 if (static_cast<pkgDepCache *>(cache_) == NULL)
1929 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1930 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1933 - (Database *) init {
1934 if ((self = [super init]) != nil) {
1941 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1942 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1946 _assert(pipe(fds) != -1);
1949 _config->Set("APT::Keep-Fds::", cydiafd_);
1950 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1953 detachNewThreadSelector:@selector(_readCydia:)
1955 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1958 _assert(pipe(fds) != -1);
1962 detachNewThreadSelector:@selector(_readStatus:)
1964 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1967 _assert(pipe(fds) != -1);
1968 _assert(dup2(fds[0], 0) != -1);
1969 _assert(close(fds[0]) != -1);
1971 input_ = fdopen(fds[1], "a");
1973 _assert(pipe(fds) != -1);
1974 _assert(dup2(fds[1], 1) != -1);
1975 _assert(close(fds[1]) != -1);
1978 detachNewThreadSelector:@selector(_readOutput:)
1980 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1985 - (pkgCacheFile &) cache {
1989 - (pkgDepCache::Policy *) policy {
1993 - (pkgRecords *) records {
1997 - (pkgProblemResolver *) resolver {
2001 - (pkgAcquire &) fetcher {
2005 - (NSArray *) packages {
2009 - (NSArray *) sources {
2010 return [sources_ allValues];
2013 - (NSArray *) issues {
2014 if (cache_->BrokenCount() == 0)
2017 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2019 for (Package *package in packages_) {
2020 if (![package broken])
2022 pkgCache::PkgIterator pkg([package iterator]);
2024 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2025 [entry addObject:[package name]];
2026 [issues addObject:entry];
2028 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2032 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2033 pkgCache::DepIterator start;
2034 pkgCache::DepIterator end;
2035 dep.GlobOr(start, end); // ++dep
2037 if (!cache_->IsImportantDep(end))
2039 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2042 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2043 [entry addObject:failure];
2044 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2046 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2047 [failure addObject:[package name]];
2049 pkgCache::PkgIterator target(start.TargetPkg());
2050 if (target->ProvidesList != 0)
2051 [failure addObject:@"?"];
2053 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2055 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2056 else if (!cache_[target].CandidateVerIter(cache_).end())
2057 [failure addObject:@"-"];
2058 else if (target->ProvidesList == 0)
2059 [failure addObject:@"!"];
2061 [failure addObject:@"%"];
2065 if (start.TargetVer() != 0)
2066 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2077 - (void) reloadData {
2096 if (!cache_.Open(progress_, true)) {
2098 if (!_error->PopMessage(error))
2101 lprintf("cache_.Open():[%s]\n", error.c_str());
2103 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2104 [delegate_ repairWithSelector:@selector(configure)];
2105 else if (error == "The package lists or status file could not be parsed or opened.")
2106 [delegate_ repairWithSelector:@selector(update)];
2107 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2108 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2109 // else if (error == "The list of sources could not be read.")
2110 else _assert(false);
2115 now_ = [[NSDate date] retain];
2117 policy_ = new pkgDepCache::Policy();
2118 records_ = new pkgRecords(cache_);
2119 resolver_ = new pkgProblemResolver(cache_);
2120 fetcher_ = new pkgAcquire(&status_);
2123 list_ = new pkgSourceList();
2124 _assert(list_->ReadMainList());
2126 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2127 _assert(pkgApplyStatus(cache_));
2129 if (cache_->BrokenCount() != 0) {
2130 _assert(pkgFixBroken(cache_));
2131 _assert(cache_->BrokenCount() == 0);
2132 _assert(pkgMinimizeUpgrade(cache_));
2135 [sources_ removeAllObjects];
2136 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2137 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2138 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2140 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2141 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2145 [packages_ removeAllObjects];
2146 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2147 if (Package *package = [Package packageWithIterator:iterator database:self])
2148 [packages_ addObject:package];
2150 [packages_ sortUsingSelector:@selector(compareByName:)];
2153 - (void) configure {
2154 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2155 system([dpkg UTF8String]);
2163 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2164 _assert(!_error->PendingError());
2167 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2170 public pkgArchiveCleaner
2173 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2178 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2180 while (_error->PopMessage(error))
2181 lprintf("ArchiveCleaner: %s\n", error.c_str());
2186 pkgRecords records(cache_);
2188 lock_ = new FileFd();
2189 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2190 _assert(!_error->PendingError());
2193 // XXX: explain this with an error message
2194 _assert(list.ReadMainList());
2196 manager_ = (_system->CreatePM(cache_));
2197 _assert(manager_->GetArchives(fetcher_, &list, &records));
2198 _assert(!_error->PendingError());
2202 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2204 _assert(list.ReadMainList());
2205 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2206 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2209 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2214 bool failed = false;
2215 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2216 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2219 std::string uri = (*item)->DescURI();
2220 std::string error = (*item)->ErrorText;
2222 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2225 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2226 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2237 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2239 if (_error->PendingError()) {
2244 if (result == pkgPackageManager::Failed) {
2249 if (result != pkgPackageManager::Completed) {
2254 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2256 _assert(list.ReadMainList());
2257 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2258 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2261 if (![before isEqualToArray:after])
2266 _assert(pkgDistUpgrade(cache_));
2270 [self updateWithStatus:status_];
2273 - (void) updateWithStatus:(Status &)status {
2275 _assert(list.ReadMainList());
2278 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2279 _assert(!_error->PendingError());
2281 pkgAcquire fetcher(&status);
2282 _assert(list.GetIndexes(&fetcher));
2284 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2285 bool failed = false;
2286 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2287 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2288 (*item)->Finished();
2292 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2293 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2294 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2297 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2302 - (void) setDelegate:(id)delegate {
2303 delegate_ = delegate;
2304 status_.setDelegate(delegate);
2305 progress_.setDelegate(delegate);
2308 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2309 pkgIndexFile *index(NULL);
2310 list_->FindIndex(file, index);
2311 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2317 /* PopUp Windows {{{ */
2318 @interface PopUpView : UIView {
2319 _transient id delegate_;
2320 UITransitionView *transition_;
2325 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2329 @implementation PopUpView
2332 [transition_ setDelegate:nil];
2333 [transition_ release];
2339 [transition_ transition:UITransitionPushFromTop toView:nil];
2342 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2343 if (from != nil && to == nil)
2344 [self removeFromSuperview];
2347 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2348 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2349 delegate_ = delegate;
2351 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2352 [self addSubview:transition_];
2354 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2356 [view addSubview:self];
2358 [transition_ setDelegate:self];
2360 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2361 [transition_ transition:UITransitionNone toView:blank];
2362 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2369 /* Mail Composition {{{ */
2370 @interface MailToView : PopUpView {
2371 MailComposeController *controller_;
2374 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2378 @implementation MailToView
2381 [controller_ release];
2385 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2389 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2390 NSLog(@"did:%@", delivery);
2391 // [UIApp setStatusBarShowsProgress:NO];
2392 if ([controller error]){
2393 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2394 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2395 [mailAlertSheet setBodyText:[controller error]];
2396 [mailAlertSheet popupAlertAnimated:YES];
2400 - (void) showError {
2401 NSLog(@"%@", [controller_ error]);
2402 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2403 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2404 [mailAlertSheet setBodyText:[controller_ error]];
2405 [mailAlertSheet popupAlertAnimated:YES];
2408 - (void) deliverMessage { _pooled
2409 if (![controller_ deliverMessage])
2410 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2413 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2414 if ([controller_ needsDelivery])
2415 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2420 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2421 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2422 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2423 [controller_ setDelegate:self];
2424 [controller_ initializeUI];
2425 [controller_ setupForURL:url];
2427 UIView *view([controller_ view]);
2428 [overlay_ addSubview:view];
2434 /* Confirmation View {{{ */
2435 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2436 if (!iterator.end())
2437 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2438 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2440 pkgCache::PkgIterator package(dep.TargetPkg());
2443 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2450 @protocol ConfirmationViewDelegate
2455 @interface ConfirmationView : BrowserView {
2456 _transient Database *database_;
2457 UIActionSheet *essential_;
2464 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2468 @implementation ConfirmationView
2475 if (essential_ != nil)
2476 [essential_ release];
2482 [book_ popFromSuperviewAnimated:YES];
2485 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2486 NSString *context = [sheet context];
2488 if ([context isEqualToString:@"remove"])
2496 [delegate_ confirm];
2501 else if ([context isEqualToString:@"unable"])
2507 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2508 [window setValue:changes_ forKey:@"changes"];
2509 [window setValue:issues_ forKey:@"issues"];
2510 [window setValue:sizes_ forKey:@"sizes"];
2511 [super webView:sender didClearWindowObject:window forFrame:frame];
2514 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2515 if ((self = [super initWithBook:book]) != nil) {
2516 database_ = database;
2518 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2519 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2520 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2521 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2522 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2526 pkgDepCache::Policy *policy([database_ policy]);
2528 pkgCacheFile &cache([database_ cache]);
2529 NSArray *packages = [database_ packages];
2530 for (size_t i(0), e = [packages count]; i != e; ++i) {
2531 Package *package = [packages objectAtIndex:i];
2532 pkgCache::PkgIterator iterator = [package iterator];
2533 pkgDepCache::StateCache &state(cache[iterator]);
2535 NSString *name([package name]);
2537 if (state.NewInstall())
2538 [installing addObject:name];
2539 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2540 [reinstalling addObject:name];
2541 else if (state.Upgrade())
2542 [upgrading addObject:name];
2543 else if (state.Downgrade())
2544 [downgrading addObject:name];
2545 else if (state.Delete()) {
2546 if ([package essential])
2548 [removing addObject:name];
2551 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2552 substrate_ |= DepSubstrate(iterator.CurrentVer());
2557 else if (Advanced_ || true) {
2558 essential_ = [[UIActionSheet alloc]
2559 initWithTitle:@"Removing Essentials"
2560 buttons:[NSArray arrayWithObjects:
2561 @"Cancel Operation (Safe)",
2562 @"Force Removal (Unsafe)",
2564 defaultButtonIndex:0
2570 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2572 [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."];
2574 essential_ = [[UIActionSheet alloc]
2575 initWithTitle:@"Unable to Comply"
2576 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2577 defaultButtonIndex:0
2582 [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."];
2585 changes_ = [[NSArray alloc] initWithObjects:
2593 issues_ = [database_ issues];
2595 issues_ = [issues_ retain];
2597 sizes_ = [[NSArray alloc] initWithObjects:
2598 SizeString([database_ fetcher].FetchNeeded()),
2599 SizeString([database_ fetcher].PartialPresent()),
2600 SizeString([database_ cache]->UsrSize()),
2603 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2607 // XXX: replace with <title/>
2608 - (NSString *) title {
2609 return issues_ == nil ? @"Confirm Changes" : @"Cannot Comply";
2612 - (NSString *) backButtonTitle {
2616 - (NSString *) leftButtonTitle {
2620 - (NSString *) _rightButtonTitle {
2621 return issues_ == nil ? @"Confirm" : nil;
2624 - (void) _leftButtonClicked {
2628 - (void) _rightButtonClicked {
2629 if (essential_ != nil)
2630 [essential_ popupAlertAnimated:YES];
2634 [delegate_ confirm];
2641 /* Progress Data {{{ */
2642 @interface ProgressData : NSObject {
2648 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2655 @implementation ProgressData
2657 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2658 if ((self = [super init]) != nil) {
2659 selector_ = selector;
2679 /* Progress View {{{ */
2680 @interface ProgressView : UIView <
2681 ConfigurationDelegate,
2684 _transient Database *database_;
2686 UIView *background_;
2687 UITransitionView *transition_;
2689 UINavigationBar *navbar_;
2690 UIProgressBar *progress_;
2691 UITextView *output_;
2692 UITextLabel *status_;
2693 UIPushButton *close_;
2696 SHA1SumValue springlist_;
2697 SHA1SumValue sandplate_;
2699 NSTimeInterval last_;
2702 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2704 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2705 - (void) setContentView:(UIView *)view;
2708 - (void) _retachThread;
2709 - (void) _detachNewThreadData:(ProgressData *)data;
2710 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2716 @protocol ProgressViewDelegate
2717 - (void) progressViewIsComplete:(ProgressView *)sender;
2720 @implementation ProgressView
2723 [transition_ setDelegate:nil];
2724 [navbar_ setDelegate:nil];
2727 if (background_ != nil)
2728 [background_ release];
2729 [transition_ release];
2732 [progress_ release];
2739 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2740 if (bootstrap_ && from == overlay_ && to == view_)
2744 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2745 if ((self = [super initWithFrame:frame]) != nil) {
2746 database_ = database;
2747 delegate_ = delegate;
2749 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2750 [transition_ setDelegate:self];
2752 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2755 [overlay_ setBackgroundColor:[UIColor blackColor]];
2757 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2758 [background_ setBackgroundColor:[UIColor blackColor]];
2759 [self addSubview:background_];
2762 [self addSubview:transition_];
2764 CGSize navsize = [UINavigationBar defaultSize];
2765 CGRect navrect = {{0, 0}, navsize};
2767 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2768 [overlay_ addSubview:navbar_];
2770 [navbar_ setBarStyle:1];
2771 [navbar_ setDelegate:self];
2773 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2774 [navbar_ pushNavigationItem:navitem];
2776 CGRect bounds = [overlay_ bounds];
2777 CGSize prgsize = [UIProgressBar defaultSize];
2780 (bounds.size.width - prgsize.width) / 2,
2781 bounds.size.height - prgsize.height - 20
2784 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2785 [progress_ setStyle:0];
2787 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2789 bounds.size.height - prgsize.height - 50,
2790 bounds.size.width - 20,
2794 [status_ setColor:[UIColor whiteColor]];
2795 [status_ setBackgroundColor:[UIColor clearColor]];
2797 [status_ setCentersHorizontally:YES];
2798 //[status_ setFont:font];
2800 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2802 navrect.size.height + 20,
2803 bounds.size.width - 20,
2804 bounds.size.height - navsize.height - 62 - navrect.size.height
2807 //[output_ setTextFont:@"Courier New"];
2808 [output_ setTextSize:12];
2810 [output_ setTextColor:[UIColor whiteColor]];
2811 [output_ setBackgroundColor:[UIColor clearColor]];
2813 [output_ setMarginTop:0];
2814 [output_ setAllowsRubberBanding:YES];
2815 [output_ setEditable:NO];
2817 [overlay_ addSubview:output_];
2819 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2821 bounds.size.height - prgsize.height - 50,
2822 bounds.size.width - 20,
2826 [close_ setAutosizesToFit:NO];
2827 [close_ setDrawsShadow:YES];
2828 [close_ setStretchBackground:YES];
2829 [close_ setEnabled:YES];
2831 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2832 [close_ setTitleFont:bold];
2834 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2835 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2836 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2840 - (void) setContentView:(UIView *)view {
2841 view_ = [view retain];
2844 - (void) resetView {
2845 [transition_ transition:6 toView:view_];
2848 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2849 NSString *context = [sheet context];
2850 if ([context isEqualToString:@"conffile"]) {
2851 FILE *input = [database_ input];
2855 fprintf(input, "N\n");
2859 fprintf(input, "Y\n");
2870 - (void) closeButtonPushed {
2879 [delegate_ suspendWithAnimation:YES];
2883 system("launchctl stop com.apple.SpringBoard");
2887 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
2896 - (void) _retachThread {
2897 UINavigationItem *item = [navbar_ topItem];
2898 [item setTitle:@"Complete"];
2900 [overlay_ addSubview:close_];
2901 [progress_ removeFromSuperview];
2902 [status_ removeFromSuperview];
2904 [delegate_ progressViewIsComplete:self];
2907 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
2908 MMap mmap(file, MMap::ReadOnly);
2910 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2911 if (!(sandplate_ == sha1.Result()))
2916 FileFd file(SpringBoard_, FileFd::ReadOnly);
2917 MMap mmap(file, MMap::ReadOnly);
2919 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2920 if (!(springlist_ == sha1.Result()))
2925 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2926 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2927 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2928 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2929 case 4: [close_ setTitle:@"Reboot Device"]; break;
2932 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
2934 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
2935 [cache autorelease];
2937 NSFileManager *manager = [NSFileManager defaultManager];
2938 NSError *error = nil;
2940 id system = [cache objectForKey:@"System"];
2945 if (stat(Cache_, &info) == -1)
2948 [system removeAllObjects];
2950 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
2951 for (NSString *app in apps)
2952 if ([app hasSuffix:@".app"]) {
2953 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
2954 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
2955 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
2957 [info setObject:path forKey:@"Path"];
2958 [info setObject:@"System" forKey:@"ApplicationType"];
2959 [system addInfoDictionary:info];
2964 [cache writeToFile:@Cache_ atomically:YES];
2966 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
2968 if (chmod(Cache_, info.st_mode) == -1)
2972 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
2975 notify_post("com.apple.mobile.application_installed");
2977 [delegate_ setStatusBarShowsProgress:NO];
2980 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
2981 [[data target] performSelector:[data selector] withObject:[data object]];
2984 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2987 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2988 UINavigationItem *item = [navbar_ topItem];
2989 [item setTitle:title];
2991 [status_ setText:nil];
2992 [output_ setText:@""];
2993 [progress_ setProgress:0];
2996 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
2998 [close_ removeFromSuperview];
2999 [overlay_ addSubview:progress_];
3000 [overlay_ addSubview:status_];
3002 [delegate_ setStatusBarShowsProgress:YES];
3006 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3007 MMap mmap(file, MMap::ReadOnly);
3009 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3010 sandplate_ = sha1.Result();
3014 FileFd file(SpringBoard_, FileFd::ReadOnly);
3015 MMap mmap(file, MMap::ReadOnly);
3017 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3018 springlist_ = sha1.Result();
3021 [transition_ transition:6 toView:overlay_];
3024 detachNewThreadSelector:@selector(_detachNewThreadData:)
3026 withObject:[[ProgressData alloc]
3027 initWithSelector:selector
3034 - (void) repairWithSelector:(SEL)selector {
3036 detachNewThreadSelector:selector
3043 - (void) setConfigurationData:(NSString *)data {
3045 performSelectorOnMainThread:@selector(_setConfigurationData:)
3051 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3052 Package *package = id == nil ? nil : [database_ packageWithName:id];
3054 UIActionSheet *sheet = [[[UIActionSheet alloc]
3055 initWithTitle:(package == nil ? id : [package name])
3056 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3057 defaultButtonIndex:0
3062 [sheet setBodyText:error];
3063 [sheet popupAlertAnimated:YES];
3066 - (void) setProgressTitle:(NSString *)title {
3068 performSelectorOnMainThread:@selector(_setProgressTitle:)
3074 - (void) setProgressPercent:(float)percent {
3076 performSelectorOnMainThread:@selector(_setProgressPercent:)
3077 withObject:[NSNumber numberWithFloat:percent]
3082 - (void) startProgress {
3083 last_ = [NSDate timeIntervalSinceReferenceDate];
3086 - (void) addProgressOutput:(NSString *)output {
3088 performSelectorOnMainThread:@selector(_addProgressOutput:)
3094 - (bool) isCancelling:(size_t)received {
3096 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3097 if (received_ != received) {
3098 received_ = received;
3100 } else if (now - last_ > 30)
3107 - (void) _setConfigurationData:(NSString *)data {
3108 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3110 _assert(conffile_r(data));
3112 NSString *ofile = conffile_r[1];
3113 //NSString *nfile = conffile_r[2];
3115 UIActionSheet *sheet = [[[UIActionSheet alloc]
3116 initWithTitle:@"Configuration Upgrade"
3117 buttons:[NSArray arrayWithObjects:
3118 @"Keep My Old Copy",
3119 @"Accept The New Copy",
3120 // XXX: @"See What Changed",
3122 defaultButtonIndex:0
3127 [sheet setBodyText:[NSString stringWithFormat:
3128 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3131 [sheet popupAlertAnimated:YES];
3134 - (void) _setProgressTitle:(NSString *)title {
3135 [status_ setText:title];
3138 - (void) _setProgressPercent:(NSNumber *)percent {
3139 [progress_ setProgress:[percent floatValue]];
3142 - (void) _addProgressOutput:(NSString *)output {
3143 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3144 CGSize size = [output_ contentSize];
3145 CGRect rect = {{0, size.height}, {size.width, 0}};
3146 [output_ scrollRectToVisible:rect animated:YES];
3149 - (BOOL) isRunning {
3156 /* Package Cell {{{ */
3157 @interface PackageCell : UISimpleTableCell {
3160 NSString *description_;
3164 UITextLabel *status_;
3168 - (PackageCell *) init;
3169 - (void) setPackage:(Package *)package;
3171 + (int) heightForPackage:(Package *)package;
3175 @implementation PackageCell
3177 - (void) clearPackage {
3188 if (description_ != nil) {
3189 [description_ release];
3193 if (source_ != nil) {
3198 if (badge_ != nil) {
3205 [self clearPackage];
3212 - (PackageCell *) init {
3213 if ((self = [super init]) != nil) {
3215 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3216 [status_ setBackgroundColor:[UIColor clearColor]];
3217 [status_ setFont:small];
3222 - (void) setPackage:(Package *)package {
3223 [self clearPackage];
3225 Source *source = [package source];
3226 NSString *section = [package section];
3228 section = Simplify(section);
3230 icon_ = [[package icon] retain];
3232 name_ = [[package name] retain];
3233 description_ = [[package tagline] retain];
3235 NSString *label = nil;
3236 bool trusted = false;
3238 if (source != nil) {
3239 label = [source label];
3240 trusted = [source trusted];
3241 } else if ([[package id] isEqualToString:@"firmware"])
3244 label = @"Unknown/Local";
3246 NSString *from = [NSString stringWithFormat:@"from %@", label];
3248 if (section != nil && ![section isEqualToString:label])
3249 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3251 source_ = [from retain];
3253 if (NSString *purpose = [package primaryPurpose])
3254 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3255 badge_ = [badge_ retain];
3258 if (NSString *mode = [package mode]) {
3259 [badge_ setImage:[UIImage applicationImageNamed:
3260 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3263 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3264 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3265 } else if ([package half]) {
3266 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3267 [status_ setText:@"Package Damaged"];
3268 [status_ setColor:[UIColor redColor]];
3270 [badge_ setImage:nil];
3271 [status_ setText:nil];
3276 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3279 rect.size = [icon_ size];
3281 rect.size.width /= 2;
3282 rect.size.height /= 2;
3284 rect.origin.x = 25 - rect.size.width / 2;
3285 rect.origin.y = 25 - rect.size.height / 2;
3287 [icon_ drawInRect:rect];
3290 if (badge_ != nil) {
3291 CGSize size = [badge_ size];
3293 [badge_ drawAtPoint:CGPointMake(
3294 36 - size.width / 2,
3295 36 - size.height / 2
3304 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3305 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3309 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3311 [super drawContentInRect:rect selected:selected];
3314 + (int) heightForPackage:(Package *)package {
3315 NSString *tagline([package tagline]);
3316 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3318 if ([package hasMode] || [package half])
3327 /* Section Cell {{{ */
3328 @interface SectionCell : UISimpleTableCell {
3333 _UISwitchSlider *switch_;
3338 - (void) setSection:(Section *)section editing:(BOOL)editing;
3342 @implementation SectionCell
3344 - (void) clearSection {
3345 if (section_ != nil) {
3355 if (count_ != nil) {
3362 [self clearSection];
3369 if ((self = [super init]) != nil) {
3370 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3372 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3373 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3377 - (void) onSwitch:(id)sender {
3378 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3379 if (metadata == nil) {
3380 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3381 [Sections_ setObject:metadata forKey:section_];
3385 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3388 - (void) setSection:(Section *)section editing:(BOOL)editing {
3389 if (editing != editing_) {
3391 [switch_ removeFromSuperview];
3393 [self addSubview:switch_];
3397 [self clearSection];
3399 if (section == nil) {
3400 name_ = [@"All Packages" retain];
3403 section_ = [section name];
3404 if (section_ != nil)
3405 section_ = [section_ retain];
3406 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3407 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3410 [switch_ setValue:isSectionVisible(section_) animated:NO];
3414 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3415 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3422 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3424 CGSize size = [count_ sizeWithFont:Font14_];
3428 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3430 [super drawContentInRect:rect selected:selected];
3436 /* File Table {{{ */
3437 @interface FileTable : RVPage {
3438 _transient Database *database_;
3441 NSMutableArray *files_;
3445 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3446 - (void) setPackage:(Package *)package;
3450 @implementation FileTable
3453 if (package_ != nil)
3462 - (int) numberOfRowsInTable:(UITable *)table {
3463 return files_ == nil ? 0 : [files_ count];
3466 - (float) table:(UITable *)table heightForRow:(int)row {
3470 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3471 if (reusing == nil) {
3472 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3473 UIFont *font = [UIFont systemFontOfSize:16];
3474 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3476 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3480 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3484 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3485 if ((self = [super initWithBook:book]) != nil) {
3486 database_ = database;
3488 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3490 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3491 [self addSubview:list_];
3493 UITableColumn *column = [[[UITableColumn alloc]
3494 initWithTitle:@"Name"
3496 width:[self frame].size.width
3499 [list_ setDataSource:self];
3500 [list_ setSeparatorStyle:1];
3501 [list_ addTableColumn:column];
3502 [list_ setDelegate:self];
3503 [list_ setReusesTableCells:YES];
3507 - (void) setPackage:(Package *)package {
3508 if (package_ != nil) {
3509 [package_ autorelease];
3518 [files_ removeAllObjects];
3520 if (package != nil) {
3521 package_ = [package retain];
3522 name_ = [[package id] retain];
3524 if (NSArray *files = [package files])
3525 [files_ addObjectsFromArray:files];
3527 if ([files_ count] != 0) {
3528 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3529 [files_ removeObjectAtIndex:0];
3530 [files_ sortUsingSelector:@selector(compareByPath:)];
3532 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3533 [stack addObject:@"/"];
3535 for (int i(0), e([files_ count]); i != e; ++i) {
3536 NSString *file = [files_ objectAtIndex:i];
3537 while (![file hasPrefix:[stack lastObject]])
3538 [stack removeLastObject];
3539 NSString *directory = [stack lastObject];
3540 [stack addObject:[file stringByAppendingString:@"/"]];
3541 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3542 ([stack count] - 2) * 3, "",
3543 [file substringFromIndex:[directory length]]
3552 - (void) resetViewAnimated:(BOOL)animated {
3553 [list_ resetViewAnimated:animated];
3556 - (void) reloadData {
3557 [self setPackage:[database_ packageWithName:name_]];
3558 [self reloadButtons];
3561 - (NSString *) title {
3562 return @"Installed Files";
3565 - (NSString *) backButtonTitle {
3571 /* Package View {{{ */
3572 @interface PackageView : BrowserView {
3573 _transient Database *database_;
3576 NSMutableArray *buttons_;
3579 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3580 - (void) setPackage:(Package *)package;
3584 @implementation PackageView
3587 if (package_ != nil)
3595 - (void) _clickButtonWithName:(NSString *)name {
3596 if ([name isEqualToString:@"Install"])
3597 [delegate_ installPackage:package_];
3598 else if ([name isEqualToString:@"Reinstall"])
3599 [delegate_ installPackage:package_];
3600 else if ([name isEqualToString:@"Remove"])
3601 [delegate_ removePackage:package_];
3602 else if ([name isEqualToString:@"Upgrade"])
3603 [delegate_ installPackage:package_];
3604 else _assert(false);
3607 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3608 int count = [buttons_ count];
3609 _assert(count != 0);
3610 _assert(button <= count + 1);
3612 if (count != button - 1)
3613 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3618 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3619 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3620 return [super webView:sender didFinishLoadForFrame:frame];
3623 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3624 [window setValue:package_ forKey:@"package"];
3625 [super webView:sender didClearWindowObject:window forFrame:frame];
3629 - (void) _rightButtonClicked {
3630 /*[super _rightButtonClicked];
3633 int count = [buttons_ count];
3634 _assert(count != 0);
3637 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3639 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3640 [buttons addObjectsFromArray:buttons_];
3641 [buttons addObject:@"Cancel"];
3643 [delegate_ slideUp:[[[UIActionSheet alloc]
3646 defaultButtonIndex:2
3654 - (NSString *) _rightButtonTitle {
3655 int count = [buttons_ count];
3656 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3659 - (NSString *) backButtonTitle {
3663 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3664 if ((self = [super initWithBook:book]) != nil) {
3665 database_ = database;
3666 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3670 - (void) setPackage:(Package *)package {
3671 if (package_ != nil) {
3672 [package_ autorelease];
3681 [buttons_ removeAllObjects];
3683 if (package != nil) {
3684 package_ = [package retain];
3685 name_ = [[package id] retain];
3687 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3689 if ([package_ source] == nil);
3690 else if ([package_ upgradableAndEssential:NO])
3691 [buttons_ addObject:@"Upgrade"];
3692 else if ([package_ installed] == nil)
3693 [buttons_ addObject:@"Install"];
3695 [buttons_ addObject:@"Reinstall"];
3696 if ([package_ installed] != nil)
3697 [buttons_ addObject:@"Remove"];
3701 - (void) reloadData {
3702 [self setPackage:[database_ packageWithName:name_]];
3703 [self reloadButtons];
3708 /* Package Table {{{ */
3709 @interface PackageTable : RVPage {
3710 _transient Database *database_;
3714 NSMutableArray *packages_;
3715 NSMutableArray *sections_;
3716 UISectionList *list_;
3719 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3721 - (void) setDelegate:(id)delegate;
3722 - (void) setObject:(id)object;
3724 - (void) reloadData;
3725 - (void) resetCursor;
3727 - (UISectionList *) list;
3729 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3733 @implementation PackageTable
3736 [list_ setDataSource:nil];
3741 [packages_ release];
3742 [sections_ release];
3747 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3748 return [sections_ count];
3751 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3752 return [[sections_ objectAtIndex:section] name];
3755 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3756 return [[sections_ objectAtIndex:section] row];
3759 - (int) numberOfRowsInTable:(UITable *)table {
3760 return [packages_ count];
3763 - (float) table:(UITable *)table heightForRow:(int)row {
3764 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3767 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3769 reusing = [[[PackageCell alloc] init] autorelease];
3770 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3774 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3778 - (void) tableRowSelected:(NSNotification *)notification {
3779 int row = [[notification object] selectedRow];
3783 Package *package = [packages_ objectAtIndex:row];
3784 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3785 [view setDelegate:delegate_];
3786 [view setPackage:package];
3787 [book_ pushPage:view];
3790 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3791 if ((self = [super initWithBook:book]) != nil) {
3792 database_ = database;
3793 title_ = [title retain];
3795 object_ = object == nil ? nil : [object retain];
3797 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3798 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3800 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3801 [list_ setDataSource:self];
3803 UITableColumn *column = [[[UITableColumn alloc]
3804 initWithTitle:@"Name"
3806 width:[self frame].size.width
3809 UITable *table = [list_ table];
3810 [table setSeparatorStyle:1];
3811 [table addTableColumn:column];
3812 [table setDelegate:self];
3813 [table setReusesTableCells:YES];
3815 [self addSubview:list_];
3818 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3819 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3823 - (void) setDelegate:(id)delegate {
3824 delegate_ = delegate;
3827 - (void) setObject:(id)object {
3833 object_ = [object retain];
3836 - (void) reloadData {
3837 NSArray *packages = [database_ packages];
3839 [packages_ removeAllObjects];
3840 [sections_ removeAllObjects];
3842 for (size_t i(0); i != [packages count]; ++i) {
3843 Package *package([packages objectAtIndex:i]);
3844 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3845 [packages_ addObject:package];
3848 Section *section = nil;
3850 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3851 Package *package = [packages_ objectAtIndex:offset];
3852 NSString *name = [package index];
3854 if (section == nil || ![[section name] isEqualToString:name]) {
3855 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3856 [sections_ addObject:section];
3859 [section addToCount];
3865 - (NSString *) title {
3869 - (void) resetViewAnimated:(BOOL)animated {
3870 [list_ resetViewAnimated:animated];
3873 - (void) resetCursor {
3874 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3877 - (UISectionList *) list {
3881 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3882 [list_ setShouldHideHeaderInShortLists:hide];
3888 /* Add Source View {{{ */
3889 @interface AddSourceView : RVPage {
3890 _transient Database *database_;
3893 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3897 @implementation AddSourceView
3899 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3900 if ((self = [super initWithBook:book]) != nil) {
3901 database_ = database;
3907 /* Source Cell {{{ */
3908 @interface SourceCell : UITableCell {
3911 NSString *description_;
3917 - (SourceCell *) initWithSource:(Source *)source;
3921 @implementation SourceCell
3926 [description_ release];
3931 - (SourceCell *) initWithSource:(Source *)source {
3932 if ((self = [super init]) != nil) {
3934 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3936 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3937 icon_ = [icon_ retain];
3939 origin_ = [[source name] retain];
3940 label_ = [[source uri] retain];
3941 description_ = [[source description] retain];
3945 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3947 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3954 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3958 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3962 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3964 [super drawContentInRect:rect selected:selected];
3969 /* Source Table {{{ */
3970 @interface SourceTable : RVPage {
3971 _transient Database *database_;
3972 UISectionList *list_;
3973 NSMutableArray *sources_;
3974 UIActionSheet *alert_;
3978 UIProgressHUD *hud_;
3981 //NSURLConnection *installer_;
3982 NSURLConnection *trivial_bz2_;
3983 NSURLConnection *trivial_gz_;
3984 //NSURLConnection *automatic_;
3989 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3993 @implementation SourceTable
3995 - (void) _deallocConnection:(NSURLConnection *)connection {
3996 if (connection != nil) {
3997 [connection cancel];
3998 //[connection setDelegate:nil];
3999 [connection release];
4004 [[list_ table] setDelegate:nil];
4005 [list_ setDataSource:nil];
4014 //[self _deallocConnection:installer_];
4015 [self _deallocConnection:trivial_gz_];
4016 [self _deallocConnection:trivial_bz2_];
4017 //[self _deallocConnection:automatic_];
4024 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4025 return offset_ == 0 ? 1 : 2;
4028 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4029 switch (section + (offset_ == 0 ? 1 : 0)) {
4030 case 0: return @"Entered by User";
4031 case 1: return @"Installed by Packages";
4039 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4040 switch (section + (offset_ == 0 ? 1 : 0)) {
4042 case 1: return offset_;
4050 - (int) numberOfRowsInTable:(UITable *)table {
4051 return [sources_ count];
4054 - (float) table:(UITable *)table heightForRow:(int)row {
4055 Source *source = [sources_ objectAtIndex:row];
4056 return [source description] == nil ? 56 : 73;
4059 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4060 Source *source = [sources_ objectAtIndex:row];
4061 // XXX: weird warning, stupid selectors ;P
4062 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4065 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4069 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4073 - (void) tableRowSelected:(NSNotification*)notification {
4074 UITable *table([list_ table]);
4075 int row([table selectedRow]);
4079 Source *source = [sources_ objectAtIndex:row];
4081 PackageTable *packages = [[[PackageTable alloc]
4084 title:[source label]
4085 filter:@selector(isVisibleInSource:)
4089 [packages setDelegate:delegate_];
4091 [book_ pushPage:packages];
4094 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4095 Source *source = [sources_ objectAtIndex:row];
4096 return [source record] != nil;
4099 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4100 [[list_ table] setDeleteConfirmationRow:row];
4103 - (void) table:(UITable *)table deleteRow:(int)row {
4104 Source *source = [sources_ objectAtIndex:row];
4105 [Sources_ removeObjectForKey:[source key]];
4106 [delegate_ syncData];
4109 - (void) _endConnection:(NSURLConnection *)connection {
4110 NSURLConnection **field = NULL;
4111 if (connection == trivial_bz2_)
4112 field = &trivial_bz2_;
4113 else if (connection == trivial_gz_)
4114 field = &trivial_gz_;
4115 _assert(field != NULL);
4116 [connection release];
4120 trivial_bz2_ == nil &&
4123 [delegate_ setStatusBarShowsProgress:NO];
4126 [hud_ removeFromSuperview];
4131 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4134 @"./", @"Distribution",
4135 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4137 [delegate_ syncData];
4138 } else if (error_ != nil) {
4139 UIActionSheet *sheet = [[[UIActionSheet alloc]
4140 initWithTitle:@"Verification Error"
4141 buttons:[NSArray arrayWithObjects:@"OK", nil]
4142 defaultButtonIndex:0
4147 [sheet setBodyText:[error_ localizedDescription]];
4148 [sheet popupAlertAnimated:YES];
4150 UIActionSheet *sheet = [[[UIActionSheet alloc]
4151 initWithTitle:@"Did not Find Repository"
4152 buttons:[NSArray arrayWithObjects:@"OK", nil]
4153 defaultButtonIndex:0
4158 [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."];
4159 [sheet popupAlertAnimated:YES];
4165 if (error_ != nil) {
4172 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4173 switch ([response statusCode]) {
4179 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4180 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4182 error_ = [error retain];
4183 [self _endConnection:connection];
4186 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4187 [self _endConnection:connection];
4190 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4191 NSMutableURLRequest *request = [NSMutableURLRequest
4192 requestWithURL:[NSURL URLWithString:href]
4193 cachePolicy:NSURLRequestUseProtocolCachePolicy
4194 timeoutInterval:20.0
4197 [request setHTTPMethod:method];
4199 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4202 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4203 NSString *context = [sheet context];
4204 if ([context isEqualToString:@"source"])
4207 NSString *href = [[sheet textField] text];
4209 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4211 if (![href hasSuffix:@"/"])
4212 href_ = [href stringByAppendingString:@"/"];
4215 href_ = [href_ retain];
4217 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4218 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4219 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4223 hud_ = [delegate_ addProgressHUD];
4224 [hud_ setText:@"Verifying URL"];
4237 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4238 if ((self = [super initWithBook:book]) != nil) {
4239 database_ = database;
4240 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4242 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4243 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4244 [list_ setShouldHideHeaderInShortLists:NO];
4246 [self addSubview:list_];
4247 [list_ setDataSource:self];
4249 UITableColumn *column = [[UITableColumn alloc]
4250 initWithTitle:@"Name"
4252 width:[self frame].size.width
4255 UITable *table = [list_ table];
4256 [table setSeparatorStyle:1];
4257 [table addTableColumn:column];
4258 [table setDelegate:self];
4262 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4263 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4267 - (void) reloadData {
4269 _assert(list.ReadMainList());
4271 [sources_ removeAllObjects];
4272 [sources_ addObjectsFromArray:[database_ sources]];
4273 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4275 int count = [sources_ count];
4276 for (offset_ = 0; offset_ != count; ++offset_) {
4277 Source *source = [sources_ objectAtIndex:offset_];
4278 if ([source record] == nil)
4285 - (void) resetViewAnimated:(BOOL)animated {
4286 [list_ resetViewAnimated:animated];
4289 - (void) _leftButtonClicked {
4290 /*[book_ pushPage:[[[AddSourceView alloc]
4295 UIActionSheet *sheet = [[[UIActionSheet alloc]
4296 initWithTitle:@"Enter Cydia/APT URL"
4297 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4298 defaultButtonIndex:0
4303 [sheet addTextFieldWithValue:@"http://" label:@""];
4305 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4306 [traits setAutocapitalizationType:0];
4307 [traits setKeyboardType:3];
4308 [traits setAutocorrectionType:1];
4310 [sheet popupAlertAnimated:YES];
4313 - (void) _rightButtonClicked {
4314 UITable *table = [list_ table];
4315 BOOL editing = [table isRowDeletionEnabled];
4316 [table enableRowDeletion:!editing animated:YES];
4317 [book_ reloadButtonsForPage:self];
4320 - (NSString *) title {
4324 - (NSString *) leftButtonTitle {
4325 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4328 - (NSString *) rightButtonTitle {
4329 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4332 - (UINavigationButtonStyle) rightButtonStyle {
4333 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4339 /* Installed View {{{ */
4340 @interface InstalledView : RVPage {
4341 _transient Database *database_;
4342 PackageTable *packages_;
4346 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4350 @implementation InstalledView
4353 [packages_ release];
4357 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4358 if ((self = [super initWithBook:book]) != nil) {
4359 database_ = database;
4361 packages_ = [[PackageTable alloc]
4365 filter:@selector(isInstalledAndVisible:)
4366 with:[NSNumber numberWithBool:YES]
4369 [self addSubview:packages_];
4371 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4372 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4376 - (void) resetViewAnimated:(BOOL)animated {
4377 [packages_ resetViewAnimated:animated];
4380 - (void) reloadData {
4381 [packages_ reloadData];
4384 - (void) _rightButtonClicked {
4385 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4386 [packages_ reloadData];
4388 [book_ reloadButtonsForPage:self];
4391 - (NSString *) title {
4392 return @"Installed";
4395 - (NSString *) backButtonTitle {
4399 - (NSString *) rightButtonTitle {
4400 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4403 - (UINavigationButtonStyle) rightButtonStyle {
4404 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4407 - (void) setDelegate:(id)delegate {
4408 [super setDelegate:delegate];
4409 [packages_ setDelegate:delegate];
4416 @interface HomeView : BrowserView {
4421 @implementation HomeView
4423 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4427 - (void) _leftButtonClicked {
4428 UIActionSheet *sheet = [[[UIActionSheet alloc]
4429 initWithTitle:@"About Cydia Installer"
4430 buttons:[NSArray arrayWithObjects:@"Close", nil]
4431 defaultButtonIndex:0
4437 @"Copyright (C) 2008\n"
4438 "Jay Freeman (saurik)\n"
4439 "saurik@saurik.com\n"
4440 "http://www.saurik.com/\n"
4443 "http://www.theokorigroup.com/\n"
4445 "College of Creative Studies,\n"
4446 "University of California,\n"
4448 "http://www.ccs.ucsb.edu/"
4451 [sheet popupAlertAnimated:YES];
4454 - (NSString *) leftButtonTitle {
4460 /* Manage View {{{ */
4461 @interface ManageView : BrowserView {
4466 @implementation ManageView
4468 - (NSString *) title {
4472 - (void) _leftButtonClicked {
4473 [delegate_ askForSettings];
4476 - (NSString *) leftButtonTitle {
4480 - (NSString *) _rightButtonTitle {
4487 @interface WebView (Cydia)
4488 - (void) setScriptDebugDelegate:(id)delegate;
4489 - (void) _setFormDelegate:(id)delegate;
4490 - (void) _setUIKitDelegate:(id)delegate;
4491 - (void) setWebMailDelegate:(id)delegate;
4492 - (void) _setLayoutInterval:(float)interval;
4495 /* Indirect Delegate {{{ */
4496 @interface IndirectDelegate : NSProxy {
4497 _transient volatile id delegate_;
4500 - (void) setDelegate:(id)delegate;
4501 - (id) initWithDelegate:(id)delegate;
4504 @implementation IndirectDelegate
4506 - (void) setDelegate:(id)delegate {
4507 delegate_ = delegate;
4510 - (id) initWithDelegate:(id)delegate {
4511 delegate_ = delegate;
4515 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4516 if (delegate_ != nil)
4517 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4519 // XXX: I fucking hate Apple so very very bad
4520 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4523 - (void) forwardInvocation:(NSInvocation *)inv {
4524 SEL sel = [inv selector];
4525 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4526 [inv invokeWithTarget:delegate_];
4531 /* Browser Implementation {{{ */
4532 @implementation BrowserView
4535 WebView *webview = [webview_ webView];
4536 [webview setFrameLoadDelegate:nil];
4537 [webview setResourceLoadDelegate:nil];
4538 [webview setUIDelegate:nil];
4539 [webview setScriptDebugDelegate:nil];
4540 [webview setPolicyDelegate:nil];
4542 [webview setDownloadDelegate:nil];
4544 [webview _setFormDelegate:nil];
4545 [webview _setUIKitDelegate:nil];
4546 [webview setWebMailDelegate:nil];
4547 [webview setEditingDelegate:nil];
4549 [webview_ setDelegate:nil];
4550 [webview_ setGestureDelegate:nil];
4552 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4557 [webview_ removeFromSuperview];
4558 [Documents_ addObject:[webview_ autorelease]];
4563 [indirect_ setDelegate:nil];
4564 [indirect_ release];
4566 [scroller_ setDelegate:nil];
4568 [scroller_ release];
4570 [indicator_ release];
4576 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4577 [self loadRequest:[NSURLRequest
4580 timeoutInterval:30.0
4584 - (void) loadURL:(NSURL *)url {
4585 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4588 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4589 NSMutableURLRequest *copy = [request mutableCopy];
4591 if (Machine_ != NULL)
4592 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4593 if (UniqueID_ != nil)
4594 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4597 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4602 - (void) loadRequest:(NSURLRequest *)request {
4604 [webview_ loadRequest:request];
4607 - (void) reloadURL {
4608 if ([urls_ count] == 0)
4610 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4611 [urls_ removeLastObject];
4612 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4615 - (WebView *) webView {
4616 return [webview_ webView];
4619 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4620 [scroller_ setContentSize:frame.size];
4623 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4624 [self view:sender didSetFrame:frame];
4627 - (void) pushPage:(RVPage *)page {
4628 [self setBackButtonTitle:title_];
4629 [page setDelegate:delegate_];
4630 [book_ pushPage:page];
4633 - (BOOL) getSpecial:(NSURL *)url {
4634 NSString *href([url absoluteString]);
4635 NSString *scheme([[url scheme] lowercaseString]);
4639 if ([href hasPrefix:@"apptapp://package/"])
4640 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4641 else if ([scheme isEqualToString:@"cydia"]) {
4642 page = [delegate_ pageForURL:url hasTag:NULL];
4645 } else if (![scheme isEqualToString:@"apptapp"])
4649 [self pushPage:page];
4653 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4654 [window setValue:delegate_ forKey:@"cydia"];
4657 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4658 if (NSURL *url = [request URL]) {
4659 if (![self getSpecial:url]) {
4660 NSString *scheme([[url scheme] lowercaseString]);
4661 if ([scheme isEqualToString:@"mailto"])
4662 [delegate_ openMailToURL:url];
4671 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4672 NSURL *url([request URL]);
4674 if (url == nil) use: {
4679 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4682 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4683 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4686 [UIApp openURL:url];
4692 int store(_not(int));
4693 if (NSURL *itms = [url itmsURL:&store]) {
4695 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4696 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4703 NSString *scheme([[url scheme] lowercaseString]);
4705 if ([scheme isEqualToString:@"tel"]) {
4706 // XXX: intelligence
4710 if ([scheme isEqualToString:@"mailto"]) {
4711 [delegate_ openMailToURL:url];
4715 if ([self getSpecial:url])
4717 else if ([WebView _canHandleRequest:request])
4719 else if ([url isSpringboardHandledURL])
4725 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4726 //lprintf("Status:%s\n", [text UTF8String]);
4729 - (void) _pushPage {
4733 [book_ pushPage:self];
4736 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4737 NSURL *url = [request URL];
4738 if ([self getSpecial:url])
4741 return [self _addHeadersToRequest:request];
4744 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4745 [self setBackButtonTitle:title_];
4747 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4748 [browser setDelegate:delegate_];
4751 [browser loadRequest:[self _addHeadersToRequest:request]];
4752 [book_ pushPage:browser];
4755 return [browser webView];
4758 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4759 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4762 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4763 return [self _createWebViewWithRequest:request pushed:YES];
4766 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4767 if ([frame parentFrame] != nil)
4770 title_ = [title retain];
4771 [book_ reloadTitleForPage:self];
4774 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4775 if ([frame parentFrame] != nil)
4780 [indicator_ startAnimation];
4781 [self reloadButtons];
4783 if (title_ != nil) {
4788 [book_ reloadTitleForPage:self];
4790 WebView *webview = [webview_ webView];
4791 NSString *href = [webview mainFrameURL];
4792 [urls_ addObject:[NSURL URLWithString:href]];
4794 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4796 CGRect webrect = [scroller_ bounds];
4797 webrect.size.height = 0;
4798 [webview_ setFrame:webrect];
4801 - (void) _finishLoading {
4804 [indicator_ stopAnimation];
4805 [self reloadButtons];
4809 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4810 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4813 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4814 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4817 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4818 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4821 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4822 return [webview_ webView:sender didCommitLoadForFrame:frame];
4825 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4826 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4829 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4830 if ([frame parentFrame] == nil)
4831 [self _finishLoading];
4832 return [webview_ webView:sender didFinishLoadForFrame:frame];
4835 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4836 if ([frame parentFrame] != nil)
4838 [self _finishLoading];
4840 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4841 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4842 [[error localizedDescription] stringByAddingPercentEscapes]
4846 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
4848 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
4852 - (id) initWithBook:(RVBook *)book {
4853 if ((self = [super initWithBook:book]) != nil) {
4856 struct CGRect bounds = [self bounds];
4858 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4859 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4860 [self addSubview:pinstripe];
4862 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4863 [self addSubview:scroller_];
4865 [scroller_ setScrollingEnabled:YES];
4866 [scroller_ setAdjustForContentSizeChange:YES];
4867 [scroller_ setClipsSubviews:YES];
4868 [scroller_ setAllowsRubberBanding:YES];
4869 [scroller_ setScrollDecelerationFactor:0.99];
4870 [scroller_ setDelegate:self];
4872 CGRect webrect = [scroller_ bounds];
4873 webrect.size.height = 0;
4878 webview_ = [Documents_ lastObject];
4879 if (webview_ != nil) {
4880 webview_ = [webview_ retain];
4881 webview = [webview_ webView];
4882 [Documents_ removeLastObject];
4883 [webview_ setFrame:webrect];
4888 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
4889 webview = [webview_ webView];
4891 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4893 [webview_ setAllowsMessaging:YES];
4895 [webview_ setTilingEnabled:YES];
4896 [webview_ setDrawsGrid:NO];
4897 [webview_ setLogsTilingChanges:NO];
4898 [webview_ setTileMinificationFilter:kCAFilterNearest];
4899 [webview_ setDetectsPhoneNumbers:NO];
4900 [webview_ setAutoresizes:YES];
4902 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4903 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4904 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4906 [webview_ _setDocumentType:0x4];
4908 [webview_ setZoomsFocusedFormControl:YES];
4909 [webview_ setContentsPosition:7];
4910 [webview_ setEnabledGestures:0xa];
4911 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4912 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4914 [webview_ setSmoothsFonts:YES];
4916 [webview _setUsesLoaderCache:YES];
4917 [webview setGroupName:@"Cydia"];
4918 //[webview _setLayoutInterval:0.5];
4921 [webview_ setDelegate:self];
4922 [webview_ setGestureDelegate:self];
4923 [scroller_ addSubview:webview_];
4925 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4927 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
4928 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
4929 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
4931 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
4932 NSString *application = package == nil ? @"Cydia" : [NSString
4933 stringWithFormat:@"Cydia/%@",
4935 ]; [webview setApplicationNameForUserAgent:application];
4937 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
4939 [webview setFrameLoadDelegate:self];
4940 [webview setResourceLoadDelegate:indirect_];
4941 [webview setUIDelegate:self];
4942 [webview setScriptDebugDelegate:self];
4943 [webview setPolicyDelegate:self];
4945 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4947 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4948 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4949 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4953 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4954 [webview_ redrawScaledDocument];
4957 - (void) _rightButtonClicked {
4962 - (NSString *) _rightButtonTitle {
4966 - (NSString *) rightButtonTitle {
4967 return loading_ ? @"" : [self _rightButtonTitle];
4970 - (NSString *) title {
4971 return title_ == nil ? @"Loading" : title_;
4974 - (NSString *) backButtonTitle {
4978 - (void) setPageActive:(BOOL)active {
4980 [indicator_ removeFromSuperview];
4982 [[book_ navigationBar] addSubview:indicator_];
4985 - (void) resetViewAnimated:(BOOL)animated {
4988 - (void) setPushed:(bool)pushed {
4995 @interface CYBook : RVBook <
4998 _transient Database *database_;
4999 UINavigationBar *overlay_;
5000 UINavigationBar *underlay_;
5001 UIProgressIndicator *indicator_;
5002 UITextLabel *prompt_;
5003 UIProgressBar *progress_;
5004 UINavigationButton *cancel_;
5007 NSTimeInterval last_;
5010 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5016 /* Install View {{{ */
5017 @interface InstallView : RVPage {
5018 _transient Database *database_;
5019 NSMutableArray *sections_;
5020 NSMutableArray *filtered_;
5021 UITransitionView *transition_;
5027 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5028 - (void) reloadData;
5033 @implementation InstallView
5036 [list_ setDataSource:nil];
5037 [list_ setDelegate:nil];
5039 [sections_ release];
5040 [filtered_ release];
5041 [transition_ release];
5043 [accessory_ release];
5047 - (int) numberOfRowsInTable:(UITable *)table {
5048 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5051 - (float) table:(UITable *)table heightForRow:(int)row {
5055 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5057 reusing = [[[SectionCell alloc] init] autorelease];
5058 [(SectionCell *)reusing setSection:(editing_ ?
5059 [sections_ objectAtIndex:row] :
5060 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5061 ) editing:editing_];
5065 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5069 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5073 - (void) tableRowSelected:(NSNotification *)notification {
5074 int row = [[notification object] selectedRow];
5085 title = @"All Packages";
5087 section = [filtered_ objectAtIndex:(row - 1)];
5088 name = [section name];
5094 title = @"(No Section)";
5098 PackageTable *table = [[[PackageTable alloc]
5102 filter:@selector(isVisiblyUninstalledInSection:)
5106 [table setDelegate:delegate_];
5108 [book_ pushPage:table];
5111 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5112 if ((self = [super initWithBook:book]) != nil) {
5113 database_ = database;
5115 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5116 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5118 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5119 [self addSubview:transition_];
5121 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5122 [transition_ transition:0 toView:list_];
5124 UITableColumn *column = [[[UITableColumn alloc]
5125 initWithTitle:@"Name"
5127 width:[self frame].size.width
5130 [list_ setDataSource:self];
5131 [list_ setSeparatorStyle:1];
5132 [list_ addTableColumn:column];
5133 [list_ setDelegate:self];
5134 [list_ setReusesTableCells:YES];
5138 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5139 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5143 - (void) reloadData {
5144 NSArray *packages = [database_ packages];
5146 [sections_ removeAllObjects];
5147 [filtered_ removeAllObjects];
5149 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5150 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5152 for (size_t i(0); i != [packages count]; ++i) {
5153 Package *package([packages objectAtIndex:i]);
5154 NSString *name([package section]);
5157 Section *section([sections objectForKey:name]);
5158 if (section == nil) {
5159 section = [[[Section alloc] initWithName:name] autorelease];
5160 [sections setObject:section forKey:name];
5164 if ([package valid] && [package installed] == nil && [package visible])
5165 [filtered addObject:package];
5168 [sections_ addObjectsFromArray:[sections allValues]];
5169 [sections_ sortUsingSelector:@selector(compareByName:)];
5171 [filtered sortUsingSelector:@selector(compareBySection:)];
5173 Section *section = nil;
5174 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5175 Package *package = [filtered objectAtIndex:offset];
5176 NSString *name = [package section];
5178 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5179 section = name == nil ?
5180 [[[Section alloc] initWithName:nil] autorelease] :
5181 [sections objectForKey:name];
5182 [filtered_ addObject:section];
5185 [section addToCount];
5191 - (void) resetView {
5193 [self _rightButtonClicked];
5196 - (void) resetViewAnimated:(BOOL)animated {
5197 [list_ resetViewAnimated:animated];
5200 - (void) _rightButtonClicked {
5201 if ((editing_ = !editing_))
5204 [delegate_ updateData];
5207 [book_ reloadTitleForPage:self];
5208 [book_ reloadButtonsForPage:self];
5211 - (NSString *) title {
5212 return editing_ ? @"Section Visibility" : @"Install by Section";
5215 - (NSString *) backButtonTitle {
5219 - (NSString *) rightButtonTitle {
5220 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5223 - (UINavigationButtonStyle) rightButtonStyle {
5224 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5227 - (UIView *) accessoryView {
5233 /* Changes View {{{ */
5234 @interface ChangesView : RVPage {
5235 _transient Database *database_;
5236 NSMutableArray *packages_;
5237 NSMutableArray *sections_;
5238 UISectionList *list_;
5242 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5243 - (void) reloadData;
5247 @implementation ChangesView
5250 [[list_ table] setDelegate:nil];
5251 [list_ setDataSource:nil];
5253 [packages_ release];
5254 [sections_ release];
5259 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5260 return [sections_ count];
5263 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5264 return [[sections_ objectAtIndex:section] name];
5267 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5268 return [[sections_ objectAtIndex:section] row];
5271 - (int) numberOfRowsInTable:(UITable *)table {
5272 return [packages_ count];
5275 - (float) table:(UITable *)table heightForRow:(int)row {
5276 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5279 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5281 reusing = [[[PackageCell alloc] init] autorelease];
5282 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5286 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5290 - (void) tableRowSelected:(NSNotification *)notification {
5291 int row = [[notification object] selectedRow];
5294 Package *package = [packages_ objectAtIndex:row];
5295 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5296 [view setDelegate:delegate_];
5297 [view setPackage:package];
5298 [book_ pushPage:view];
5301 - (void) _leftButtonClicked {
5302 [(CYBook *)book_ update];
5303 [self reloadButtons];
5306 - (void) _rightButtonClicked {
5307 [delegate_ distUpgrade];
5310 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5311 if ((self = [super initWithBook:book]) != nil) {
5312 database_ = database;
5314 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5315 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5317 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5318 [self addSubview:list_];
5320 [list_ setShouldHideHeaderInShortLists:NO];
5321 [list_ setDataSource:self];
5322 //[list_ setSectionListStyle:1];
5324 UITableColumn *column = [[[UITableColumn alloc]
5325 initWithTitle:@"Name"
5327 width:[self frame].size.width
5330 UITable *table = [list_ table];
5331 [table setSeparatorStyle:1];
5332 [table addTableColumn:column];
5333 [table setDelegate:self];
5334 [table setReusesTableCells:YES];
5338 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5339 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5343 - (void) reloadData {
5344 NSArray *packages = [database_ packages];
5346 [packages_ removeAllObjects];
5347 [sections_ removeAllObjects];
5349 for (size_t i(0); i != [packages count]; ++i) {
5350 Package *package([packages objectAtIndex:i]);
5353 [package installed] == nil && [package valid] && [package visible] ||
5354 [package upgradableAndEssential:NO]
5356 [packages_ addObject:package];
5359 [packages_ sortUsingSelector:@selector(compareForChanges:)];
5361 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5362 Section *section = nil;
5365 bool unseens = false;
5367 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5369 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5370 Package *package = [packages_ objectAtIndex:offset];
5372 if ([package upgradableAndEssential:YES]) {
5374 [upgradable addToCount];
5377 NSDate *seen = [package seen];
5382 name = [@"n/a ?" retain];
5384 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
5387 if (section == nil || ![[section name] isEqualToString:name]) {
5388 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5389 [sections_ addObject:section];
5393 [section addToCount];
5397 CFRelease(formatter);
5400 Section *last = [sections_ lastObject];
5401 size_t count = [last count];
5402 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5403 [sections_ removeLastObject];
5407 [sections_ insertObject:upgradable atIndex:0];
5410 [self reloadButtons];
5413 - (void) resetViewAnimated:(BOOL)animated {
5414 [list_ resetViewAnimated:animated];
5417 - (NSString *) leftButtonTitle {
5418 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5421 - (NSString *) rightButtonTitle {
5422 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5425 - (NSString *) title {
5431 /* Search View {{{ */
5432 @protocol SearchViewDelegate
5433 - (void) showKeyboard:(BOOL)show;
5436 @interface SearchView : RVPage {
5438 UISearchField *field_;
5439 UITransitionView *transition_;
5440 PackageTable *table_;
5441 UIPreferencesTable *advanced_;
5447 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5448 - (void) reloadData;
5452 @implementation SearchView
5455 [field_ setDelegate:nil];
5457 [accessory_ release];
5459 [transition_ release];
5461 [advanced_ release];
5466 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5470 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5472 case 0: return @"Advanced Search (Coming Soon!)";
5474 default: _assert(false);
5478 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5482 default: _assert(false);
5486 - (void) _showKeyboard:(BOOL)show {
5487 CGSize keysize = [UIKeyboard defaultSize];
5488 CGRect keydown = [book_ pageBounds];
5489 CGRect keyup = keydown;
5490 keyup.size.height -= keysize.height - ButtonBarHeight_;
5492 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5494 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5495 [animation setSignificantRectFields:8];
5498 [animation setStartFrame:keydown];
5499 [animation setEndFrame:keyup];
5501 [animation setStartFrame:keyup];
5502 [animation setEndFrame:keydown];
5505 UIAnimator *animator = [UIAnimator sharedAnimator];
5508 addAnimations:[NSArray arrayWithObjects:animation, nil]
5509 withDuration:(KeyboardTime_ - delay)
5514 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5516 [delegate_ showKeyboard:show];
5519 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5520 [self _showKeyboard:YES];
5523 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5524 [self _showKeyboard:NO];
5527 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5529 NSString *text([field_ text]);
5530 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5536 - (void) textFieldClearButtonPressed:(UITextField *)field {
5540 - (void) keyboardInputShouldDelete:(id)input {
5544 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5545 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5549 [field_ resignFirstResponder];
5554 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5555 if ((self = [super initWithBook:book]) != nil) {
5556 CGRect pageBounds = [book_ pageBounds];
5558 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5559 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5560 [self addSubview:pinstripe];*/
5562 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5563 [self addSubview:transition_];
5565 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5567 [advanced_ setReusesTableCells:YES];
5568 [advanced_ setDataSource:self];
5569 [advanced_ reloadData];
5571 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5572 CGColor dimmed(space_, 0, 0, 0, 0.5);
5573 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5575 table_ = [[PackageTable alloc]
5579 filter:@selector(isUnfilteredAndSearchedForBy:)
5583 [table_ setShouldHideHeaderInShortLists:NO];
5584 [transition_ transition:0 toView:table_];
5593 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5600 [self bounds].size.width - area.origin.x - 18;
5602 area.size.height = [UISearchField defaultHeight];
5604 field_ = [[UISearchField alloc] initWithFrame:area];
5606 UIFont *font = [UIFont systemFontOfSize:16];
5607 [field_ setFont:font];
5609 [field_ setPlaceholder:@"Package Names & Descriptions"];
5610 [field_ setDelegate:self];
5612 [field_ setPaddingTop:5];
5614 UITextInputTraits *traits = [field_ textInputTraits];
5615 [traits setAutocapitalizationType:0];
5616 [traits setAutocorrectionType:1];
5617 [traits setReturnKeyType:6];
5619 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5621 accessory_ = [[UIView alloc] initWithFrame:accrect];
5622 [accessory_ addSubview:field_];
5624 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5625 [configure setShowPressFeedback:YES];
5626 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5627 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5628 [accessory_ addSubview:configure];*/
5630 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5631 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5637 LKAnimation *animation = [LKTransition animation];
5638 [animation setType:@"oglFlip"];
5639 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5640 [animation setFillMode:@"extended"];
5641 [animation setTransitionFlags:3];
5642 [animation setDuration:10];
5643 [animation setSpeed:0.35];
5644 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5645 [[transition_ _layer] addAnimation:animation forKey:0];
5646 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5647 flipped_ = !flipped_;
5651 - (void) configurePushed {
5652 [field_ resignFirstResponder];
5656 - (void) resetViewAnimated:(BOOL)animated {
5659 [table_ resetViewAnimated:animated];
5662 - (void) _reloadData {
5665 - (void) reloadData {
5668 [table_ setObject:[field_ text]];
5669 [table_ reloadData];
5670 [table_ resetCursor];
5673 - (UIView *) accessoryView {
5677 - (NSString *) title {
5681 - (NSString *) backButtonTitle {
5685 - (void) setDelegate:(id)delegate {
5686 [table_ setDelegate:delegate];
5687 [super setDelegate:delegate];
5693 @implementation CYBook
5697 [indicator_ release];
5699 [progress_ release];
5704 - (NSString *) getTitleForPage:(RVPage *)page {
5705 return Simplify([super getTitleForPage:page]);
5713 [UIView beginAnimations:nil context:NULL];
5715 CGRect ovrframe = [overlay_ frame];
5716 ovrframe.origin.y = 0;
5717 [overlay_ setFrame:ovrframe];
5719 CGRect barframe = [navbar_ frame];
5720 barframe.origin.y += ovrframe.size.height;
5721 [navbar_ setFrame:barframe];
5723 CGRect trnframe = [transition_ frame];
5724 trnframe.origin.y += ovrframe.size.height;
5725 trnframe.size.height -= ovrframe.size.height;
5726 [transition_ setFrame:trnframe];
5728 [UIView endAnimations];
5730 [indicator_ startAnimation];
5731 [prompt_ setText:@"Updating Database"];
5732 [progress_ setProgress:0];
5735 last_ = [NSDate timeIntervalSinceReferenceDate];
5737 [overlay_ addSubview:cancel_];
5740 detachNewThreadSelector:@selector(_update)
5749 [indicator_ stopAnimation];
5751 [UIView beginAnimations:nil context:NULL];
5753 CGRect ovrframe = [overlay_ frame];
5754 ovrframe.origin.y = -ovrframe.size.height;
5755 [overlay_ setFrame:ovrframe];
5757 CGRect barframe = [navbar_ frame];
5758 barframe.origin.y -= ovrframe.size.height;
5759 [navbar_ setFrame:barframe];
5761 CGRect trnframe = [transition_ frame];
5762 trnframe.origin.y -= ovrframe.size.height;
5763 trnframe.size.height += ovrframe.size.height;
5764 [transition_ setFrame:trnframe];
5766 [UIView commitAnimations];
5768 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5771 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5772 if ((self = [super initWithFrame:frame]) != nil) {
5773 database_ = database;
5775 CGRect ovrrect = [navbar_ bounds];
5776 ovrrect.size.height = [UINavigationBar defaultSize].height;
5777 ovrrect.origin.y = -ovrrect.size.height;
5779 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5780 [self addSubview:overlay_];
5782 ovrrect.origin.y = frame.size.height;
5783 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5784 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5785 [self addSubview:underlay_];
5787 [overlay_ setBarStyle:1];
5788 [underlay_ setBarStyle:1];
5790 int barstyle = [overlay_ _barStyle:NO];
5791 bool ugly = barstyle == 0;
5793 UIProgressIndicatorStyle style = ugly ?
5794 UIProgressIndicatorStyleMediumBrown :
5795 UIProgressIndicatorStyleMediumWhite;
5797 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5798 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5799 CGRect indrect = {{indoffset, indoffset}, indsize};
5801 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5802 [indicator_ setStyle:style];
5803 [overlay_ addSubview:indicator_];
5805 CGSize prmsize = {215, indsize.height + 4};
5808 indoffset * 2 + indsize.width,
5812 unsigned(ovrrect.size.height - prmsize.height) / 2
5815 UIFont *font = [UIFont systemFontOfSize:15];
5817 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5819 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5820 [prompt_ setBackgroundColor:[UIColor clearColor]];
5821 [prompt_ setFont:font];
5823 [overlay_ addSubview:prompt_];
5825 CGSize prgsize = {75, 100};
5828 ovrrect.size.width - prgsize.width - 10,
5829 (ovrrect.size.height - prgsize.height) / 2
5832 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5833 [progress_ setStyle:0];
5834 [overlay_ addSubview:progress_];
5836 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5837 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5839 CGRect frame = [cancel_ frame];
5840 frame.size.width = 65;
5841 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5842 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5843 [cancel_ setFrame:frame];
5845 [cancel_ setBarStyle:barstyle];
5849 - (void) _onCancel {
5851 [cancel_ removeFromSuperview];
5854 - (void) _update { _pooled
5856 status.setDelegate(self);
5858 [database_ updateWithStatus:status];
5861 performSelectorOnMainThread:@selector(_update_)
5867 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5868 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5871 - (void) setProgressTitle:(NSString *)title {
5873 performSelectorOnMainThread:@selector(_setProgressTitle:)
5879 - (void) setProgressPercent:(float)percent {
5881 performSelectorOnMainThread:@selector(_setProgressPercent:)
5882 withObject:[NSNumber numberWithFloat:percent]
5887 - (void) startProgress {
5890 - (void) addProgressOutput:(NSString *)output {
5892 performSelectorOnMainThread:@selector(_addProgressOutput:)
5898 - (bool) isCancelling:(size_t)received {
5899 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5900 if (received_ != received) {
5901 received_ = received;
5903 } else if (now - last_ > 15)
5908 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5912 - (void) _setProgressTitle:(NSString *)title {
5913 [prompt_ setText:title];
5916 - (void) _setProgressPercent:(NSNumber *)percent {
5917 [progress_ setProgress:[percent floatValue]];
5920 - (void) _addProgressOutput:(NSString *)output {
5925 @interface CydiaURLProtocol : NSURLProtocol {
5930 @implementation CydiaURLProtocol
5932 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5933 NSURL *url([request URL]);
5936 NSString *scheme([[url scheme] lowercaseString]);
5937 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5942 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5946 - (void) startLoading {
5947 id<NSURLProtocolClient> client([self client]);
5948 NSURLRequest *request([self request]);
5950 NSURL *url([request URL]);
5951 NSString *href([url absoluteString]);
5953 NSString *path([href substringFromIndex:8]);
5954 NSRange slash([path rangeOfString:@"/"]);
5957 if (slash.location == NSNotFound) {
5961 command = [path substringToIndex:slash.location];
5962 path = [path substringFromIndex:(slash.location + 1)];
5965 Database *database([Database sharedInstance]);
5967 if ([command isEqualToString:@"package-icon"]) {
5970 Package *package([database packageWithName:path]);
5974 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5976 UIImage *icon([package icon]);
5977 NSData *data(UIImagePNGRepresentation(icon));
5979 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5980 [client URLProtocol:self didLoadData:data];
5981 [client URLProtocolDidFinishLoading:self];
5983 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5987 - (void) stopLoading {
5992 @interface SignatureView : BrowserView {
5993 _transient Database *database_;
5997 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6001 @implementation SignatureView
6008 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6010 [super webView:sender didClearWindowObject:window forFrame:frame];
6013 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6014 if ((self = [super initWithBook:book]) != nil) {
6015 database_ = database;
6016 package_ = [package retain];
6021 - (void) reloadData {
6022 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6027 @interface Cydia : UIApplication <
6028 ConfirmationViewDelegate,
6029 ProgressViewDelegate,
6038 UIToolbar *buttonbar_;
6042 NSMutableArray *essential_;
6043 NSMutableArray *broken_;
6045 Database *database_;
6046 ProgressView *progress_;
6050 UIKeyboard *keyboard_;
6051 UIProgressHUD *hud_;
6053 InstallView *install_;
6054 ChangesView *changes_;
6055 ManageView *manage_;
6056 SearchView *search_;
6061 @implementation Cydia
6064 if ([broken_ count] != 0) {
6065 int count = [broken_ count];
6067 UIActionSheet *sheet = [[[UIActionSheet alloc]
6068 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6069 buttons:[NSArray arrayWithObjects:
6071 @"Ignore (Temporary)",
6073 defaultButtonIndex:0
6078 [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."];
6079 [sheet popupAlertAnimated:YES];
6080 } else if (!Ignored_ && [essential_ count] != 0) {
6081 int count = [essential_ count];
6083 UIActionSheet *sheet = [[[UIActionSheet alloc]
6084 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6085 buttons:[NSArray arrayWithObjects:
6086 @"Upgrade Essential",
6087 @"Upgrade Everything",
6088 @"Ignore (Temporary)",
6090 defaultButtonIndex:0
6095 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6096 [sheet popupAlertAnimated:YES];
6100 - (void) _reloadData {
6101 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6102 [hud setText:@"Reloading Data"];
6103 [overlay_ addSubview:hud];
6106 [database_ reloadData];
6110 [essential_ removeAllObjects];
6111 [broken_ removeAllObjects];
6113 NSArray *packages = [database_ packages];
6114 for (Package *package in packages) {
6116 [broken_ addObject:package];
6117 if ([package upgradableAndEssential:NO]) {
6118 if ([package essential])
6119 [essential_ addObject:package];
6125 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6126 [buttonbar_ setBadgeValue:badge forButton:3];
6127 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6128 [buttonbar_ setBadgeAnimated:YES forButton:3];
6129 [self setApplicationBadge:badge];
6131 [buttonbar_ setBadgeValue:nil forButton:3];
6132 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6133 [buttonbar_ setBadgeAnimated:NO forButton:3];
6134 [self removeApplicationBadge];
6140 if ([packages count] == 0);
6152 [hud removeFromSuperview];*/
6155 - (void) _saveConfig {
6157 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6162 - (void) updateData {
6165 /* XXX: this is just stupid */
6167 [install_ reloadData];
6169 [changes_ reloadData];
6171 [search_ reloadData];
6181 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6182 _assert(file != NULL);
6184 NSArray *keys = [Sources_ allKeys];
6186 for (int i(0), e([keys count]); i != e; ++i) {
6187 NSString *key = [keys objectAtIndex:i];
6188 NSDictionary *source = [Sources_ objectForKey:key];
6190 fprintf(file, "%s %s %s\n",
6191 [[source objectForKey:@"Type"] UTF8String],
6192 [[source objectForKey:@"URI"] UTF8String],
6193 [[source objectForKey:@"Distribution"] UTF8String]
6202 detachNewThreadSelector:@selector(update_)
6205 title:@"Updating Sources"
6209 - (void) reloadData {
6210 @synchronized (self) {
6211 if (confirm_ == nil)
6217 pkgProblemResolver *resolver = [database_ resolver];
6219 resolver->InstallProtect();
6220 if (!resolver->Resolve(true))
6225 [database_ prepare];
6227 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6228 [confirm_ setDelegate:self];
6230 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6231 [page setDelegate:self];
6233 [confirm_ setPage:page];
6234 [underlay_ popSubview:confirm_];
6237 - (void) installPackage:(Package *)package {
6238 @synchronized (self) {
6245 - (void) removePackage:(Package *)package {
6246 @synchronized (self) {
6253 - (void) distUpgrade {
6254 @synchronized (self) {
6255 [database_ upgrade];
6261 @synchronized (self) {
6263 if (confirm_ != nil) {
6271 [overlay_ removeFromSuperview];
6275 detachNewThreadSelector:@selector(perform)
6282 - (void) bootstrap_ {
6284 [database_ upgrade];
6285 [database_ prepare];
6286 [database_ perform];
6289 - (void) bootstrap {
6291 detachNewThreadSelector:@selector(bootstrap_)
6294 title:@"Bootstrap Install"
6298 - (void) progressViewIsComplete:(ProgressView *)progress {
6299 if (confirm_ != nil) {
6300 [underlay_ addSubview:overlay_];
6301 [confirm_ popFromSuperviewAnimated:NO];
6307 - (void) setPage:(RVPage *)page {
6308 [page resetViewAnimated:NO];
6309 [page setDelegate:self];
6310 [book_ setPage:page];
6313 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6314 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6315 [browser loadURL:url];
6319 - (void) _setHomePage {
6320 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6323 - (void) buttonBarItemTapped:(id)sender {
6324 unsigned tag = [sender tag];
6326 [book_ resetViewAnimated:YES];
6328 } else if (tag_ == 2 && tag != 2)
6329 [install_ resetView];
6332 case 1: [self _setHomePage]; break;
6334 case 2: [self setPage:install_]; break;
6335 case 3: [self setPage:changes_]; break;
6336 case 4: [self setPage:manage_]; break;
6337 case 5: [self setPage:search_]; break;
6339 default: _assert(false);
6345 - (void) applicationWillSuspend {
6347 [super applicationWillSuspend];
6350 - (void) askForSettings {
6351 UIActionSheet *role = [[[UIActionSheet alloc]
6352 initWithTitle:@"Who Are You?"
6353 buttons:[NSArray arrayWithObjects:
6354 @"User (Graphical Only)",
6355 @"Hacker (+ Command Line)",
6356 @"Developer (No Filters)",
6358 defaultButtonIndex:-1
6363 [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."];
6364 [role popupAlertAnimated:YES];
6369 [self setStatusBarShowsProgress:NO];
6372 [hud_ removeFromSuperview];
6376 pid_t pid = ExecFork();
6378 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6379 perror("launchctl stop");
6386 [self askForSettings];
6390 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6392 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6393 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6394 0, 0, screenrect.size.width, screenrect.size.height - 48
6395 ) database:database_];
6397 [book_ setDelegate:self];
6399 [overlay_ addSubview:book_];
6401 NSArray *buttonitems = [NSArray arrayWithObjects:
6402 [NSDictionary dictionaryWithObjectsAndKeys:
6403 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6404 @"home-up.png", kUIButtonBarButtonInfo,
6405 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6406 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6407 self, kUIButtonBarButtonTarget,
6408 @"Home", kUIButtonBarButtonTitle,
6409 @"0", kUIButtonBarButtonType,
6412 [NSDictionary dictionaryWithObjectsAndKeys:
6413 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6414 @"install-up.png", kUIButtonBarButtonInfo,
6415 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6416 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6417 self, kUIButtonBarButtonTarget,
6418 @"Sections", kUIButtonBarButtonTitle,
6419 @"0", kUIButtonBarButtonType,
6422 [NSDictionary dictionaryWithObjectsAndKeys:
6423 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6424 @"changes-up.png", kUIButtonBarButtonInfo,
6425 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6426 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6427 self, kUIButtonBarButtonTarget,
6428 @"Changes", kUIButtonBarButtonTitle,
6429 @"0", kUIButtonBarButtonType,
6432 [NSDictionary dictionaryWithObjectsAndKeys:
6433 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6434 @"manage-up.png", kUIButtonBarButtonInfo,
6435 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6436 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6437 self, kUIButtonBarButtonTarget,
6438 @"Manage", kUIButtonBarButtonTitle,
6439 @"0", kUIButtonBarButtonType,
6442 [NSDictionary dictionaryWithObjectsAndKeys:
6443 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6444 @"search-up.png", kUIButtonBarButtonInfo,
6445 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6446 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6447 self, kUIButtonBarButtonTarget,
6448 @"Search", kUIButtonBarButtonTitle,
6449 @"0", kUIButtonBarButtonType,
6453 buttonbar_ = [[UIToolbar alloc]
6455 withFrame:CGRectMake(
6456 0, screenrect.size.height - ButtonBarHeight_,
6457 screenrect.size.width, ButtonBarHeight_
6459 withItemList:buttonitems
6462 [buttonbar_ setDelegate:self];
6463 [buttonbar_ setBarStyle:1];
6464 [buttonbar_ setButtonBarTrackingMode:2];
6466 int buttons[5] = {1, 2, 3, 4, 5};
6467 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6468 [buttonbar_ showButtonGroup:0 withDuration:0];
6470 for (int i = 0; i != 5; ++i)
6471 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6472 i * 64 + 2, 1, 60, ButtonBarHeight_
6475 [buttonbar_ showSelectionForButton:1];
6476 [overlay_ addSubview:buttonbar_];
6478 [UIKeyboard initImplementationNow];
6479 CGSize keysize = [UIKeyboard defaultSize];
6480 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6481 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6482 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6483 [overlay_ addSubview:keyboard_];
6485 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6486 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6487 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6489 manage_ = (ManageView *) [[self
6490 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6491 withClass:[ManageView class]
6495 [underlay_ addSubview:overlay_];
6502 [self _setHomePage];
6505 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6506 NSString *context = [sheet context];
6507 if ([context isEqualToString:@"fixhalf"])
6510 @synchronized (self) {
6511 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6512 Package *broken = [broken_ objectAtIndex:i];
6515 NSString *id = [broken id];
6516 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6517 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6518 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6519 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6528 [broken_ removeAllObjects];
6535 else if ([context isEqualToString:@"role"]) {
6537 case 1: Role_ = @"User"; break;
6538 case 2: Role_ = @"Hacker"; break;
6539 case 3: Role_ = @"Developer"; break;
6546 bool reset = Settings_ != nil;
6548 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6552 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6560 } else if ([context isEqualToString:@"upgrade"])
6563 @synchronized (self) {
6564 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6565 Package *essential = [essential_ objectAtIndex:i];
6566 [essential install];
6589 - (void) reorganize { _pooled
6590 system("/usr/libexec/cydia/free.sh");
6591 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6594 - (void) applicationSuspend:(__GSEvent *)event {
6595 if (hud_ == nil && ![progress_ isRunning])
6596 [super applicationSuspend:event];
6599 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6601 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6604 - (void) _setSuspended:(BOOL)value {
6606 [super _setSuspended:value];
6609 - (UIProgressHUD *) addProgressHUD {
6610 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6612 [underlay_ addSubview:hud];
6616 - (void) openMailToURL:(NSURL *)url {
6617 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6620 - (RVPage *) pageForPackage:(NSString *)name {
6621 if (Package *package = [database_ packageWithName:name]) {
6622 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6623 [view setPackage:package];
6626 UIActionSheet *sheet = [[[UIActionSheet alloc]
6627 initWithTitle:@"Cannot Locate Package"
6628 buttons:[NSArray arrayWithObjects:@"Close", nil]
6629 defaultButtonIndex:0
6634 [sheet setBodyText:[NSString stringWithFormat:
6635 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6638 [sheet popupAlertAnimated:YES];
6643 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6644 NSString *href = [url absoluteString];
6649 if ([href isEqualToString:@"cydia://add-source"])
6650 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6651 else if ([href isEqualToString:@"cydia://sources"])
6652 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6653 else if ([href isEqualToString:@"cydia://packages"])
6654 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6655 else if ([href hasPrefix:@"cydia://url/"])
6656 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6657 else if ([href hasPrefix:@"cydia://launch/"])
6658 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6659 else if ([href hasPrefix:@"cydia://package-signature/"])
6660 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6661 else if ([href hasPrefix:@"cydia://package/"])
6662 return [self pageForPackage:[href substringFromIndex:16]];
6663 else if ([href hasPrefix:@"cydia://files/"]) {
6664 NSString *name = [href substringFromIndex:14];
6666 if (Package *package = [database_ packageWithName:name]) {
6667 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6668 [files setPackage:package];
6676 - (void) applicationOpenURL:(NSURL *)url {
6677 [super applicationOpenURL:url];
6679 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6680 [self setPage:page];
6681 [buttonbar_ showSelectionForButton:tag];
6686 - (void) applicationDidFinishLaunching:(id)unused {
6687 Font12_ = [[UIFont systemFontOfSize:12] retain];
6688 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6689 Font14_ = [[UIFont systemFontOfSize:14] retain];
6690 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6691 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6693 _assert(pkgInitConfig(*_config));
6694 _assert(pkgInitSystem(*_config, _system));
6698 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6699 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6701 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6703 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6704 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6706 [window_ orderFront:self];
6707 [window_ makeKey:self];
6708 [window_ setHidden:NO];
6710 database_ = [Database sharedInstance];
6711 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6712 [database_ setDelegate:progress_];
6713 [window_ setContentView:progress_];
6715 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6716 [progress_ setContentView:underlay_];
6718 [progress_ resetView];
6721 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6722 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6723 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6724 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6725 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6726 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6727 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6729 [self setIdleTimerDisabled:YES];
6731 hud_ = [self addProgressHUD];
6732 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6734 [self setStatusBarShowsProgress:YES];
6737 detachNewThreadSelector:@selector(reorganize)
6745 /* Web Scripting {{{ */
6746 + (NSString *) webScriptNameForSelector:(SEL)selector {
6747 if (selector == @selector(supports:))
6752 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
6753 return selector != @selector(supports:);
6756 - (BOOL) supports:(NSString *)feature {
6757 return [feature isEqualToString:@"window.open"];
6761 - (void) showKeyboard:(BOOL)show {
6762 CGSize keysize = [UIKeyboard defaultSize];
6763 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6764 CGRect keyup = keydown;
6765 keyup.origin.y -= keysize.height;
6767 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6768 [animation setSignificantRectFields:2];
6771 [animation setStartFrame:keydown];
6772 [animation setEndFrame:keyup];
6773 [keyboard_ activate];
6775 [animation setStartFrame:keyup];
6776 [animation setEndFrame:keydown];
6777 [keyboard_ deactivate];
6780 [[UIAnimator sharedAnimator]
6781 addAnimations:[NSArray arrayWithObjects:animation, nil]
6782 withDuration:KeyboardTime_
6787 - (void) slideUp:(UIActionSheet *)alert {
6789 [alert presentSheetFromButtonBar:buttonbar_];
6791 [alert presentSheetInView:overlay_];
6796 void AddPreferences(NSString *plist) { _pooled
6797 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6798 _assert(settings != NULL);
6799 NSMutableArray *items = [settings objectForKey:@"items"];
6803 for (size_t i(0); i != [items count]; ++i) {
6804 NSMutableDictionary *item([items objectAtIndex:i]);
6805 NSString *label = [item objectForKey:@"label"];
6806 if (label != nil && [label isEqualToString:@"Cydia"]) {
6813 for (size_t i(0); i != [items count]; ++i) {
6814 NSDictionary *item([items objectAtIndex:i]);
6815 NSString *label = [item objectForKey:@"label"];
6816 if (label != nil && [label isEqualToString:@"General"]) {
6817 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6818 @"CydiaSettings", @"bundle",
6819 @"PSLinkCell", @"cell",
6820 [NSNumber numberWithBool:YES], @"hasIcon",
6821 [NSNumber numberWithBool:YES], @"isController",
6823 nil] atIndex:(i + 1)];
6829 _assert([settings writeToFile:plist atomically:YES] == YES);
6834 id Alloc_(id self, SEL selector) {
6835 id object = alloc_(self, selector);
6836 lprintf("[%s]A-%p\n", self->isa->name, object);
6841 id Dealloc_(id self, SEL selector) {
6842 id object = dealloc_(self, selector);
6843 lprintf("[%s]D-%p\n", self->isa->name, object);
6847 int main(int argc, char *argv[]) { _pooled
6848 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6850 App_ = [[NSBundle mainBundle] bundlePath];
6851 Home_ = NSHomeDirectory();
6852 Locale_ = CFLocaleCopyCurrent();
6855 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6856 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6857 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6858 Sounds_Keyboard_ = [keyboard boolValue];
6864 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6865 _assert(errno == ENOENT);
6866 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6867 _assert(errno == ENOENT);
6869 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6870 alloc_ = alloc->method_imp;
6871 alloc->method_imp = (IMP) &Alloc_;*/
6873 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6874 dealloc_ = dealloc->method_imp;
6875 dealloc->method_imp = (IMP) &Dealloc_;*/
6880 size = sizeof(maxproc);
6881 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6882 perror("sysctlbyname(\"kern.maxproc\", ?)");
6883 else if (maxproc < 64) {
6885 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6886 perror("sysctlbyname(\"kern.maxproc\", #)");
6889 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6890 char *machine = new char[size];
6891 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6892 perror("sysctlbyname(\"hw.machine\", ?)");
6896 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6898 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6899 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6901 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6902 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6904 Settings_ = [Metadata_ objectForKey:@"Settings"];
6906 Packages_ = [Metadata_ objectForKey:@"Packages"];
6907 Sections_ = [Metadata_ objectForKey:@"Sections"];
6908 Sources_ = [Metadata_ objectForKey:@"Sources"];
6911 if (Settings_ != nil)
6912 Role_ = [Settings_ objectForKey:@"Role"];
6914 if (Packages_ == nil) {
6915 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6916 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6919 if (Sections_ == nil) {
6920 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6921 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6924 if (Sources_ == nil) {
6925 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6926 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6930 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
6933 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6934 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6936 if (access("/User", F_OK) != 0)
6937 system("/usr/libexec/cydia/firmware.sh");
6939 _assert([[NSFileManager defaultManager]
6940 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
6941 withIntermediateDirectories:YES
6946 space_ = CGColorSpaceCreateDeviceRGB();
6948 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6949 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6950 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6951 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
6952 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6953 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6955 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6957 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6959 UIApplicationUseLegacyEvents(YES);
6960 UIKeyboardDisableAutomaticAppearance();
6962 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6964 CGColorSpaceRelease(space_);