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 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1198 latest_ = [StripVersion(latest) retain];
1200 pkgCache::VerIterator current = iterator_.CurrentVer();
1201 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1202 installed_ = [StripVersion(installed) retain];
1204 if (!version_.end())
1205 file_ = version_.FileList();
1207 pkgCache &cache([database_ cache]);
1208 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1211 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1214 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1216 const char *begin, *end;
1217 parser->GetRec(begin, end);
1219 name_ = Scour("name", begin, end);
1221 name_ = [name_ retain];
1222 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1223 icon_ = Scour("icon", begin, end);
1225 icon_ = [icon_ retain];
1226 depiction_ = Scour("depiction", begin, end);
1227 if (depiction_ != nil)
1228 depiction_ = [depiction_ retain];
1229 homepage_ = Scour("homepage", begin, end);
1230 if (homepage_ == nil)
1231 homepage_ = Scour("website", begin, end);
1232 if ([homepage_ isEqualToString:depiction_])
1234 if (homepage_ != nil)
1235 homepage_ = [homepage_ retain];
1236 NSString *sponsor = Scour("sponsor", begin, end);
1238 sponsor_ = [[Address addressWithString:sponsor] retain];
1239 NSString *author = Scour("author", begin, end);
1241 author_ = [[Address addressWithString:author] retain];
1242 NSString *tags = Scour("tag", begin, end);
1244 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1248 for (int i(0), e([tags_ count]); i != e; ++i) {
1249 NSString *tag = [tags_ objectAtIndex:i];
1250 if ([tag hasPrefix:@"role::"]) {
1251 role_ = [[tag substringFromIndex:6] retain];
1256 NSString *solid(latest == nil ? installed : latest);
1257 bool changed(false);
1259 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1260 if (metadata == nil) {
1261 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1266 [metadata setObject:solid forKey:@"LastVersion"];
1269 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1270 NSDate *last([metadata objectForKey:@"LastSeen"]);
1271 NSString *version([metadata objectForKey:@"LastVersion"]);
1274 first = last == nil ? now_ : last;
1275 [metadata setObject:first forKey:@"FirstSeen"];
1280 if (version == nil) {
1281 [metadata setObject:solid forKey:@"LastVersion"];
1283 } else if (![version isEqualToString:solid]) {
1284 [metadata setObject:solid forKey:@"LastVersion"];
1286 [metadata setObject:last forKey:@"LastSeen"];
1292 [Packages_ setObject:metadata forKey:id_];
1298 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1299 return [[[Package alloc]
1300 initWithIterator:iterator
1305 - (pkgCache::PkgIterator) iterator {
1309 - (NSString *) section {
1310 const char *section = iterator_.Section();
1311 if (section == NULL)
1314 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1317 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1318 if (NSString *rename = [value objectForKey:@"Rename"]) {
1323 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1326 - (Address *) maintainer {
1329 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1330 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1334 return version_.end() ? 0 : version_->InstalledSize;
1337 - (NSString *) description {
1340 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1341 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1343 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1344 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1345 if ([lines count] < 2)
1348 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1349 for (size_t i(1); i != [lines count]; ++i) {
1350 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1351 [trimmed addObject:trim];
1354 return [trimmed componentsJoinedByString:@"\n"];
1357 - (NSString *) index {
1358 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1359 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1363 NSDictionary *metadata([Packages_ objectForKey:id_]);
1365 if (NSNumber *isSubscribed = [metadata objectForKey:@"IsSubscribed"])
1366 subscribed = [isSubscribed boolValue];
1370 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1372 return [metadata objectForKey:@"FirstSeen"];
1375 - (NSString *) latest {
1379 - (NSString *) installed {
1384 return !version_.end();
1387 - (BOOL) upgradableAndEssential:(BOOL)essential {
1388 pkgCache::VerIterator current = iterator_.CurrentVer();
1391 return essential && [self essential];
1393 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1394 return !candidate.end() && candidate != current;
1398 - (BOOL) essential {
1399 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1403 return [database_ cache][iterator_].InstBroken();
1406 - (BOOL) unfiltered {
1407 NSString *section = [self section];
1408 return section == nil || isSectionVisible(section);
1412 return [self hasSupportingRole] && [self unfiltered];
1416 unsigned char current = iterator_->CurrentState;
1417 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1420 - (BOOL) halfConfigured {
1421 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1424 - (BOOL) halfInstalled {
1425 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1429 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1430 return state.Mode != pkgDepCache::ModeKeep;
1433 - (NSString *) mode {
1434 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1436 switch (state.Mode) {
1437 case pkgDepCache::ModeDelete:
1438 if ((state.iFlags & pkgDepCache::Purge) != 0)
1442 case pkgDepCache::ModeKeep:
1443 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1447 case pkgDepCache::ModeInstall:
1448 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1449 return @"Reinstall";
1450 else switch (state.Status) {
1452 return @"Downgrade";
1458 return @"New Install";
1471 - (NSString *) name {
1472 return name_ == nil ? id_ : name_;
1475 - (NSString *) tagline {
1479 - (UIImage *) icon {
1480 NSString *section = [self section];
1482 section = Simplify(section);
1485 if (NSString *icon = icon_)
1486 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1487 if (icon == nil) if (section != nil)
1488 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1489 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1490 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1492 icon = [UIImage applicationImageNamed:@"unknown.png"];
1496 - (NSString *) homepage {
1500 - (NSString *) depiction {
1504 - (Address *) sponsor {
1508 - (Address *) author {
1512 - (NSArray *) files {
1513 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1514 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1517 fin.open([path UTF8String]);
1522 while (std::getline(fin, line))
1523 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1528 - (NSArray *) relationships {
1529 return relationships_;
1532 - (NSArray *) warnings {
1533 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1534 const char *name(iterator_.Name());
1536 size_t length(strlen(name));
1537 if (length < 2) invalid:
1538 [warnings addObject:@"illegal package identifier"];
1539 else for (size_t i(0); i != length; ++i)
1541 (name[i] < 'a' || name[i] > 'z') &&
1542 (name[i] < '0' || name[i] > '9') &&
1543 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1546 if (strcmp(name, "cydia") != 0) {
1550 if (NSArray *files = [self files])
1551 for (NSString *file in files)
1552 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1554 else if (!stash && [file isEqualToString:@"/var/stash"])
1558 [warnings addObject:@"files installed into Cydia.app"];
1560 [warnings addObject:@"files installed to /var/stash"];
1563 return [warnings count] == 0 ? nil : warnings;
1566 - (NSArray *) applications {
1567 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1569 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1571 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1572 if (NSArray *files = [self files])
1573 for (NSString *file in files)
1574 if (application_r(file)) {
1575 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1576 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1577 if ([id isEqualToString:me])
1580 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1582 display = application_r[1];
1584 NSString *bundle([file stringByDeletingLastPathComponent]);
1585 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1586 if (icon == nil || [icon length] == 0)
1588 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1590 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1591 [applications addObject:application];
1593 [application addObject:id];
1594 [application addObject:display];
1595 [application addObject:url];
1598 return [applications count] == 0 ? nil : applications;
1601 - (Source *) source {
1603 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1610 - (NSString *) role {
1614 - (BOOL) matches:(NSString *)text {
1620 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1621 if (range.location != NSNotFound)
1624 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1625 if (range.location != NSNotFound)
1628 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1629 if (range.location != NSNotFound)
1635 - (bool) hasSupportingRole {
1638 if ([role_ isEqualToString:@"enduser"])
1640 if ([Role_ isEqualToString:@"User"])
1642 if ([role_ isEqualToString:@"hacker"])
1644 if ([Role_ isEqualToString:@"Hacker"])
1646 if ([role_ isEqualToString:@"developer"])
1648 if ([Role_ isEqualToString:@"Developer"])
1653 - (BOOL) hasTag:(NSString *)tag {
1654 return tags_ == nil ? NO : [tags_ containsObject:tag];
1657 - (NSString *) primaryPurpose {
1658 for (NSString *tag in tags_)
1659 if ([tag hasPrefix:@"purpose::"])
1660 return [tag substringFromIndex:9];
1664 - (NSArray *) purposes {
1665 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1666 for (NSString *tag in tags_)
1667 if ([tag hasPrefix:@"purpose::"])
1668 [purposes addObject:[tag substringFromIndex:9]];
1669 return [purposes count] == 0 ? nil : purposes;
1672 - (NSComparisonResult) compareByName:(Package *)package {
1673 NSString *lhs = [self name];
1674 NSString *rhs = [package name];
1676 if ([lhs length] != 0 && [rhs length] != 0) {
1677 unichar lhc = [lhs characterAtIndex:0];
1678 unichar rhc = [rhs characterAtIndex:0];
1680 if (isalpha(lhc) && !isalpha(rhc))
1681 return NSOrderedAscending;
1682 else if (!isalpha(lhc) && isalpha(rhc))
1683 return NSOrderedDescending;
1686 return [lhs compare:rhs options:CompareOptions_];
1689 - (NSComparisonResult) compareBySection:(Package *)package {
1690 NSString *lhs = [self section];
1691 NSString *rhs = [package section];
1693 if (lhs == NULL && rhs != NULL)
1694 return NSOrderedAscending;
1695 else if (lhs != NULL && rhs == NULL)
1696 return NSOrderedDescending;
1697 else if (lhs != NULL && rhs != NULL) {
1698 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1699 if (result != NSOrderedSame)
1703 return NSOrderedSame;
1706 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1707 NSString *lhs = [self section];
1708 NSString *rhs = [package section];
1710 if (lhs == NULL && rhs != NULL)
1711 return NSOrderedAscending;
1712 else if (lhs != NULL && rhs == NULL)
1713 return NSOrderedDescending;
1714 else if (lhs != NULL && rhs != NULL) {
1715 NSComparisonResult result = [lhs compare:rhs];
1716 if (result != NSOrderedSame)
1720 return [self compareByName:package];
1723 - (NSComparisonResult) compareForChanges:(Package *)package {
1724 BOOL lhs = [self upgradableAndEssential:YES];
1725 BOOL rhs = [package upgradableAndEssential:YES];
1728 return lhs ? NSOrderedAscending : NSOrderedDescending;
1730 switch ([[self seen] compare:[package seen]]) {
1731 case NSOrderedAscending:
1732 return NSOrderedDescending;
1735 case NSOrderedDescending:
1736 return NSOrderedAscending;
1742 return [self compareByName:package];
1746 pkgProblemResolver *resolver = [database_ resolver];
1747 resolver->Clear(iterator_);
1748 resolver->Protect(iterator_);
1749 pkgCacheFile &cache([database_ cache]);
1750 cache->MarkInstall(iterator_, false);
1751 pkgDepCache::StateCache &state((*cache)[iterator_]);
1752 if (!state.Install())
1753 cache->SetReInstall(iterator_, true);
1757 pkgProblemResolver *resolver = [database_ resolver];
1758 resolver->Clear(iterator_);
1759 resolver->Protect(iterator_);
1760 resolver->Remove(iterator_);
1761 [database_ cache]->MarkDelete(iterator_, true);
1764 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1765 return [NSNumber numberWithBool:(
1766 [self unfiltered] && [self matches:search]
1770 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1771 return [NSNumber numberWithBool:(
1772 (![number boolValue] || [self visible]) && [self installed] != nil
1776 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1777 NSString *section = [self section];
1779 return [NSNumber numberWithBool:(
1781 [self installed] == nil && (
1783 section == nil && [name length] == 0 ||
1784 [name isEqualToString:section]
1789 - (NSNumber *) isVisibleInSource:(Source *)source {
1790 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1795 /* Section Class {{{ */
1796 @interface Section : NSObject {
1802 - (NSComparisonResult) compareByName:(Section *)section;
1803 - (Section *) initWithName:(NSString *)name;
1804 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1805 - (NSString *) name;
1808 - (void) addToCount;
1812 @implementation Section
1819 - (NSComparisonResult) compareByName:(Section *)section {
1820 NSString *lhs = [self name];
1821 NSString *rhs = [section name];
1823 if ([lhs length] != 0 && [rhs length] != 0) {
1824 unichar lhc = [lhs characterAtIndex:0];
1825 unichar rhc = [rhs characterAtIndex:0];
1827 if (isalpha(lhc) && !isalpha(rhc))
1828 return NSOrderedAscending;
1829 else if (!isalpha(lhc) && isalpha(rhc))
1830 return NSOrderedDescending;
1833 return [lhs compare:rhs options:CompareOptions_];
1836 - (Section *) initWithName:(NSString *)name {
1837 return [self initWithName:name row:0];
1840 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1841 if ((self = [super init]) != nil) {
1842 name_ = [name retain];
1847 - (NSString *) name {
1859 - (void) addToCount {
1867 static NSArray *Finishes_;
1869 /* Database Implementation {{{ */
1870 @implementation Database
1872 + (Database *) sharedInstance {
1873 static Database *instance;
1874 if (instance == nil)
1875 instance = [[Database alloc] init];
1884 - (void) _readCydia:(NSNumber *)fd { _pooled
1885 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1886 std::istream is(&ib);
1889 static Pcre finish_r("^finish:([^:]*)$");
1891 while (std::getline(is, line)) {
1892 const char *data(line.c_str());
1893 size_t size = line.size();
1894 lprintf("C:%s\n", data);
1896 if (finish_r(data, size)) {
1897 NSString *finish = finish_r[1];
1898 int index = [Finishes_ indexOfObject:finish];
1899 if (index != INT_MAX && index > Finish_)
1907 - (void) _readStatus:(NSNumber *)fd { _pooled
1908 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1909 std::istream is(&ib);
1912 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1913 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1915 while (std::getline(is, line)) {
1916 const char *data(line.c_str());
1917 size_t size = line.size();
1918 lprintf("S:%s\n", data);
1920 if (conffile_r(data, size)) {
1921 [delegate_ setConfigurationData:conffile_r[1]];
1922 } else if (strncmp(data, "status: ", 8) == 0) {
1923 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1924 [delegate_ setProgressTitle:string];
1925 } else if (pmstatus_r(data, size)) {
1926 std::string type([pmstatus_r[1] UTF8String]);
1927 NSString *id = pmstatus_r[2];
1929 float percent([pmstatus_r[3] floatValue]);
1930 [delegate_ setProgressPercent:(percent / 100)];
1932 NSString *string = pmstatus_r[4];
1934 if (type == "pmerror")
1935 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1936 withObject:[NSArray arrayWithObjects:string, id, nil]
1939 else if (type == "pmstatus")
1940 [delegate_ setProgressTitle:string];
1941 else if (type == "pmconffile")
1942 [delegate_ setConfigurationData:string];
1943 else _assert(false);
1944 } else _assert(false);
1950 - (void) _readOutput:(NSNumber *)fd { _pooled
1951 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1952 std::istream is(&ib);
1955 while (std::getline(is, line)) {
1956 lprintf("O:%s\n", line.c_str());
1957 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1967 - (Package *) packageWithName:(NSString *)name {
1968 if (static_cast<pkgDepCache *>(cache_) == NULL)
1970 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1971 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1974 - (Database *) init {
1975 if ((self = [super init]) != nil) {
1982 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1983 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1987 _assert(pipe(fds) != -1);
1990 _config->Set("APT::Keep-Fds::", cydiafd_);
1991 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1994 detachNewThreadSelector:@selector(_readCydia:)
1996 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1999 _assert(pipe(fds) != -1);
2003 detachNewThreadSelector:@selector(_readStatus:)
2005 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2008 _assert(pipe(fds) != -1);
2009 _assert(dup2(fds[0], 0) != -1);
2010 _assert(close(fds[0]) != -1);
2012 input_ = fdopen(fds[1], "a");
2014 _assert(pipe(fds) != -1);
2015 _assert(dup2(fds[1], 1) != -1);
2016 _assert(close(fds[1]) != -1);
2019 detachNewThreadSelector:@selector(_readOutput:)
2021 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2026 - (pkgCacheFile &) cache {
2030 - (pkgDepCache::Policy *) policy {
2034 - (pkgRecords *) records {
2038 - (pkgProblemResolver *) resolver {
2042 - (pkgAcquire &) fetcher {
2046 - (NSArray *) packages {
2050 - (NSArray *) sources {
2051 return [sources_ allValues];
2054 - (NSArray *) issues {
2055 if (cache_->BrokenCount() == 0)
2058 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2060 for (Package *package in packages_) {
2061 if (![package broken])
2063 pkgCache::PkgIterator pkg([package iterator]);
2065 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2066 [entry addObject:[package name]];
2067 [issues addObject:entry];
2069 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2073 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2074 pkgCache::DepIterator start;
2075 pkgCache::DepIterator end;
2076 dep.GlobOr(start, end); // ++dep
2078 if (!cache_->IsImportantDep(end))
2080 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2083 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2084 [entry addObject:failure];
2085 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2087 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2088 [failure addObject:[package name]];
2090 pkgCache::PkgIterator target(start.TargetPkg());
2091 if (target->ProvidesList != 0)
2092 [failure addObject:@"?"];
2094 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2096 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2097 else if (!cache_[target].CandidateVerIter(cache_).end())
2098 [failure addObject:@"-"];
2099 else if (target->ProvidesList == 0)
2100 [failure addObject:@"!"];
2102 [failure addObject:@"%"];
2106 if (start.TargetVer() != 0)
2107 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2118 - (void) reloadData {
2137 if (!cache_.Open(progress_, true)) {
2139 if (!_error->PopMessage(error))
2142 lprintf("cache_.Open():[%s]\n", error.c_str());
2144 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2145 [delegate_ repairWithSelector:@selector(configure)];
2146 else if (error == "The package lists or status file could not be parsed or opened.")
2147 [delegate_ repairWithSelector:@selector(update)];
2148 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2149 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2150 // else if (error == "The list of sources could not be read.")
2151 else _assert(false);
2156 now_ = [[NSDate date] retain];
2158 policy_ = new pkgDepCache::Policy();
2159 records_ = new pkgRecords(cache_);
2160 resolver_ = new pkgProblemResolver(cache_);
2161 fetcher_ = new pkgAcquire(&status_);
2164 list_ = new pkgSourceList();
2165 _assert(list_->ReadMainList());
2167 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2168 _assert(pkgApplyStatus(cache_));
2170 if (cache_->BrokenCount() != 0) {
2171 _assert(pkgFixBroken(cache_));
2172 _assert(cache_->BrokenCount() == 0);
2173 _assert(pkgMinimizeUpgrade(cache_));
2176 [sources_ removeAllObjects];
2177 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2178 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2179 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2181 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2182 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2186 [packages_ removeAllObjects];
2187 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2188 if (Package *package = [Package packageWithIterator:iterator database:self])
2189 [packages_ addObject:package];
2191 [packages_ sortUsingSelector:@selector(compareByName:)];
2194 - (void) configure {
2195 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2196 system([dpkg UTF8String]);
2204 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2205 _assert(!_error->PendingError());
2208 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2211 public pkgArchiveCleaner
2214 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2219 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2221 while (_error->PopMessage(error))
2222 lprintf("ArchiveCleaner: %s\n", error.c_str());
2227 pkgRecords records(cache_);
2229 lock_ = new FileFd();
2230 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2231 _assert(!_error->PendingError());
2234 // XXX: explain this with an error message
2235 _assert(list.ReadMainList());
2237 manager_ = (_system->CreatePM(cache_));
2238 _assert(manager_->GetArchives(fetcher_, &list, &records));
2239 _assert(!_error->PendingError());
2243 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2245 _assert(list.ReadMainList());
2246 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2247 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2250 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2255 bool failed = false;
2256 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2257 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2260 std::string uri = (*item)->DescURI();
2261 std::string error = (*item)->ErrorText;
2263 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2266 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2267 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2278 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2280 if (_error->PendingError()) {
2285 if (result == pkgPackageManager::Failed) {
2290 if (result != pkgPackageManager::Completed) {
2295 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2297 _assert(list.ReadMainList());
2298 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2299 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2302 if (![before isEqualToArray:after])
2307 _assert(pkgDistUpgrade(cache_));
2311 [self updateWithStatus:status_];
2314 - (void) updateWithStatus:(Status &)status {
2316 _assert(list.ReadMainList());
2319 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2320 _assert(!_error->PendingError());
2322 pkgAcquire fetcher(&status);
2323 _assert(list.GetIndexes(&fetcher));
2325 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2326 bool failed = false;
2327 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2328 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2329 (*item)->Finished();
2333 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2334 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2335 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2338 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2343 - (void) setDelegate:(id)delegate {
2344 delegate_ = delegate;
2345 status_.setDelegate(delegate);
2346 progress_.setDelegate(delegate);
2349 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2350 pkgIndexFile *index(NULL);
2351 list_->FindIndex(file, index);
2352 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2358 /* PopUp Windows {{{ */
2359 @interface PopUpView : UIView {
2360 _transient id delegate_;
2361 UITransitionView *transition_;
2366 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2370 @implementation PopUpView
2373 [transition_ setDelegate:nil];
2374 [transition_ release];
2380 [transition_ transition:UITransitionPushFromTop toView:nil];
2383 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2384 if (from != nil && to == nil)
2385 [self removeFromSuperview];
2388 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2389 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2390 delegate_ = delegate;
2392 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2393 [self addSubview:transition_];
2395 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2397 [view addSubview:self];
2399 [transition_ setDelegate:self];
2401 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2402 [transition_ transition:UITransitionNone toView:blank];
2403 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2410 /* Mail Composition {{{ */
2411 @interface MailToView : PopUpView {
2412 MailComposeController *controller_;
2415 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2419 @implementation MailToView
2422 [controller_ release];
2426 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2430 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2431 NSLog(@"did:%@", delivery);
2432 // [UIApp setStatusBarShowsProgress:NO];
2433 if ([controller error]){
2434 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2435 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2436 [mailAlertSheet setBodyText:[controller error]];
2437 [mailAlertSheet popupAlertAnimated:YES];
2441 - (void) showError {
2442 NSLog(@"%@", [controller_ error]);
2443 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2444 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2445 [mailAlertSheet setBodyText:[controller_ error]];
2446 [mailAlertSheet popupAlertAnimated:YES];
2449 - (void) deliverMessage { _pooled
2450 if (![controller_ deliverMessage])
2451 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2454 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2455 if ([controller_ needsDelivery])
2456 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2461 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2462 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2463 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2464 [controller_ setDelegate:self];
2465 [controller_ initializeUI];
2466 [controller_ setupForURL:url];
2468 UIView *view([controller_ view]);
2469 [overlay_ addSubview:view];
2475 /* Confirmation View {{{ */
2476 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2477 if (!iterator.end())
2478 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2479 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2481 pkgCache::PkgIterator package(dep.TargetPkg());
2484 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2491 @protocol ConfirmationViewDelegate
2496 @interface ConfirmationView : BrowserView {
2497 _transient Database *database_;
2498 UIActionSheet *essential_;
2505 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2509 @implementation ConfirmationView
2516 if (essential_ != nil)
2517 [essential_ release];
2523 [book_ popFromSuperviewAnimated:YES];
2526 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2527 NSString *context = [sheet context];
2529 if ([context isEqualToString:@"remove"])
2537 [delegate_ confirm];
2542 else if ([context isEqualToString:@"unable"])
2548 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2549 [window setValue:changes_ forKey:@"changes"];
2550 [window setValue:issues_ forKey:@"issues"];
2551 [window setValue:sizes_ forKey:@"sizes"];
2552 [super webView:sender didClearWindowObject:window forFrame:frame];
2555 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2556 if ((self = [super initWithBook:book]) != nil) {
2557 database_ = database;
2559 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2560 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2561 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2562 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2563 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2567 pkgDepCache::Policy *policy([database_ policy]);
2569 pkgCacheFile &cache([database_ cache]);
2570 NSArray *packages = [database_ packages];
2571 for (size_t i(0), e = [packages count]; i != e; ++i) {
2572 Package *package = [packages objectAtIndex:i];
2573 pkgCache::PkgIterator iterator = [package iterator];
2574 pkgDepCache::StateCache &state(cache[iterator]);
2576 NSString *name([package name]);
2578 if (state.NewInstall())
2579 [installing addObject:name];
2580 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2581 [reinstalling addObject:name];
2582 else if (state.Upgrade())
2583 [upgrading addObject:name];
2584 else if (state.Downgrade())
2585 [downgrading addObject:name];
2586 else if (state.Delete()) {
2587 if ([package essential])
2589 [removing addObject:name];
2592 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2593 substrate_ |= DepSubstrate(iterator.CurrentVer());
2598 else if (Advanced_ || true) {
2599 essential_ = [[UIActionSheet alloc]
2600 initWithTitle:@"Removing Essentials"
2601 buttons:[NSArray arrayWithObjects:
2602 @"Cancel Operation (Safe)",
2603 @"Force Removal (Unsafe)",
2605 defaultButtonIndex:0
2611 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2613 [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."];
2615 essential_ = [[UIActionSheet alloc]
2616 initWithTitle:@"Unable to Comply"
2617 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2618 defaultButtonIndex:0
2623 [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."];
2626 changes_ = [[NSArray alloc] initWithObjects:
2634 issues_ = [database_ issues];
2636 issues_ = [issues_ retain];
2638 sizes_ = [[NSArray alloc] initWithObjects:
2639 SizeString([database_ fetcher].FetchNeeded()),
2640 SizeString([database_ fetcher].PartialPresent()),
2641 SizeString([database_ cache]->UsrSize()),
2644 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2648 // XXX: replace with <title/>
2649 - (NSString *) title {
2650 return issues_ == nil ? @"Confirm Changes" : @"Cannot Comply";
2653 - (NSString *) backButtonTitle {
2657 - (NSString *) leftButtonTitle {
2661 - (NSString *) _rightButtonTitle {
2662 return issues_ == nil ? @"Confirm" : nil;
2665 - (void) _leftButtonClicked {
2669 - (void) _rightButtonClicked {
2670 if (essential_ != nil)
2671 [essential_ popupAlertAnimated:YES];
2675 [delegate_ confirm];
2682 /* Progress Data {{{ */
2683 @interface ProgressData : NSObject {
2689 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2696 @implementation ProgressData
2698 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2699 if ((self = [super init]) != nil) {
2700 selector_ = selector;
2720 /* Progress View {{{ */
2721 @interface ProgressView : UIView <
2722 ConfigurationDelegate,
2725 _transient Database *database_;
2727 UIView *background_;
2728 UITransitionView *transition_;
2730 UINavigationBar *navbar_;
2731 UIProgressBar *progress_;
2732 UITextView *output_;
2733 UITextLabel *status_;
2734 UIPushButton *close_;
2737 SHA1SumValue springlist_;
2738 SHA1SumValue sandplate_;
2740 NSTimeInterval last_;
2743 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2745 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2746 - (void) setContentView:(UIView *)view;
2749 - (void) _retachThread;
2750 - (void) _detachNewThreadData:(ProgressData *)data;
2751 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2757 @protocol ProgressViewDelegate
2758 - (void) progressViewIsComplete:(ProgressView *)sender;
2761 @implementation ProgressView
2764 [transition_ setDelegate:nil];
2765 [navbar_ setDelegate:nil];
2768 if (background_ != nil)
2769 [background_ release];
2770 [transition_ release];
2773 [progress_ release];
2780 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2781 if (bootstrap_ && from == overlay_ && to == view_)
2785 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2786 if ((self = [super initWithFrame:frame]) != nil) {
2787 database_ = database;
2788 delegate_ = delegate;
2790 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2791 [transition_ setDelegate:self];
2793 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2796 [overlay_ setBackgroundColor:[UIColor blackColor]];
2798 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2799 [background_ setBackgroundColor:[UIColor blackColor]];
2800 [self addSubview:background_];
2803 [self addSubview:transition_];
2805 CGSize navsize = [UINavigationBar defaultSize];
2806 CGRect navrect = {{0, 0}, navsize};
2808 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2809 [overlay_ addSubview:navbar_];
2811 [navbar_ setBarStyle:1];
2812 [navbar_ setDelegate:self];
2814 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2815 [navbar_ pushNavigationItem:navitem];
2817 CGRect bounds = [overlay_ bounds];
2818 CGSize prgsize = [UIProgressBar defaultSize];
2821 (bounds.size.width - prgsize.width) / 2,
2822 bounds.size.height - prgsize.height - 20
2825 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2826 [progress_ setStyle:0];
2828 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2830 bounds.size.height - prgsize.height - 50,
2831 bounds.size.width - 20,
2835 [status_ setColor:[UIColor whiteColor]];
2836 [status_ setBackgroundColor:[UIColor clearColor]];
2838 [status_ setCentersHorizontally:YES];
2839 //[status_ setFont:font];
2841 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2843 navrect.size.height + 20,
2844 bounds.size.width - 20,
2845 bounds.size.height - navsize.height - 62 - navrect.size.height
2848 //[output_ setTextFont:@"Courier New"];
2849 [output_ setTextSize:12];
2851 [output_ setTextColor:[UIColor whiteColor]];
2852 [output_ setBackgroundColor:[UIColor clearColor]];
2854 [output_ setMarginTop:0];
2855 [output_ setAllowsRubberBanding:YES];
2856 [output_ setEditable:NO];
2858 [overlay_ addSubview:output_];
2860 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2862 bounds.size.height - prgsize.height - 50,
2863 bounds.size.width - 20,
2867 [close_ setAutosizesToFit:NO];
2868 [close_ setDrawsShadow:YES];
2869 [close_ setStretchBackground:YES];
2870 [close_ setEnabled:YES];
2872 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2873 [close_ setTitleFont:bold];
2875 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2876 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2877 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2881 - (void) setContentView:(UIView *)view {
2882 view_ = [view retain];
2885 - (void) resetView {
2886 [transition_ transition:6 toView:view_];
2889 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2890 NSString *context = [sheet context];
2891 if ([context isEqualToString:@"conffile"]) {
2892 FILE *input = [database_ input];
2896 fprintf(input, "N\n");
2900 fprintf(input, "Y\n");
2911 - (void) closeButtonPushed {
2920 [delegate_ suspendWithAnimation:YES];
2924 system("launchctl stop com.apple.SpringBoard");
2928 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
2937 - (void) _retachThread {
2938 UINavigationItem *item = [navbar_ topItem];
2939 [item setTitle:@"Complete"];
2941 [overlay_ addSubview:close_];
2942 [progress_ removeFromSuperview];
2943 [status_ removeFromSuperview];
2945 [delegate_ progressViewIsComplete:self];
2948 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
2949 MMap mmap(file, MMap::ReadOnly);
2951 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2952 if (!(sandplate_ == sha1.Result()))
2957 FileFd file(SpringBoard_, FileFd::ReadOnly);
2958 MMap mmap(file, MMap::ReadOnly);
2960 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2961 if (!(springlist_ == sha1.Result()))
2966 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2967 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2968 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2969 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2970 case 4: [close_ setTitle:@"Reboot Device"]; break;
2973 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
2975 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
2976 [cache autorelease];
2978 NSFileManager *manager = [NSFileManager defaultManager];
2979 NSError *error = nil;
2981 id system = [cache objectForKey:@"System"];
2986 if (stat(Cache_, &info) == -1)
2989 [system removeAllObjects];
2991 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
2992 for (NSString *app in apps)
2993 if ([app hasSuffix:@".app"]) {
2994 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
2995 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
2996 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
2998 [info setObject:path forKey:@"Path"];
2999 [info setObject:@"System" forKey:@"ApplicationType"];
3000 [system addInfoDictionary:info];
3005 [cache writeToFile:@Cache_ atomically:YES];
3007 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3009 if (chmod(Cache_, info.st_mode) == -1)
3013 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3016 notify_post("com.apple.mobile.application_installed");
3018 [delegate_ setStatusBarShowsProgress:NO];
3021 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3022 [[data target] performSelector:[data selector] withObject:[data object]];
3025 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3028 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3029 UINavigationItem *item = [navbar_ topItem];
3030 [item setTitle:title];
3032 [status_ setText:nil];
3033 [output_ setText:@""];
3034 [progress_ setProgress:0];
3037 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3039 [close_ removeFromSuperview];
3040 [overlay_ addSubview:progress_];
3041 [overlay_ addSubview:status_];
3043 [delegate_ setStatusBarShowsProgress:YES];
3047 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3048 MMap mmap(file, MMap::ReadOnly);
3050 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3051 sandplate_ = sha1.Result();
3055 FileFd file(SpringBoard_, FileFd::ReadOnly);
3056 MMap mmap(file, MMap::ReadOnly);
3058 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3059 springlist_ = sha1.Result();
3062 [transition_ transition:6 toView:overlay_];
3065 detachNewThreadSelector:@selector(_detachNewThreadData:)
3067 withObject:[[ProgressData alloc]
3068 initWithSelector:selector
3075 - (void) repairWithSelector:(SEL)selector {
3077 detachNewThreadSelector:selector
3084 - (void) setConfigurationData:(NSString *)data {
3086 performSelectorOnMainThread:@selector(_setConfigurationData:)
3092 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3093 Package *package = id == nil ? nil : [database_ packageWithName:id];
3095 UIActionSheet *sheet = [[[UIActionSheet alloc]
3096 initWithTitle:(package == nil ? id : [package name])
3097 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3098 defaultButtonIndex:0
3103 [sheet setBodyText:error];
3104 [sheet popupAlertAnimated:YES];
3107 - (void) setProgressTitle:(NSString *)title {
3109 performSelectorOnMainThread:@selector(_setProgressTitle:)
3115 - (void) setProgressPercent:(float)percent {
3117 performSelectorOnMainThread:@selector(_setProgressPercent:)
3118 withObject:[NSNumber numberWithFloat:percent]
3123 - (void) startProgress {
3124 last_ = [NSDate timeIntervalSinceReferenceDate];
3127 - (void) addProgressOutput:(NSString *)output {
3129 performSelectorOnMainThread:@selector(_addProgressOutput:)
3135 - (bool) isCancelling:(size_t)received {
3137 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3138 if (received_ != received) {
3139 received_ = received;
3141 } else if (now - last_ > 30)
3148 - (void) _setConfigurationData:(NSString *)data {
3149 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3151 _assert(conffile_r(data));
3153 NSString *ofile = conffile_r[1];
3154 //NSString *nfile = conffile_r[2];
3156 UIActionSheet *sheet = [[[UIActionSheet alloc]
3157 initWithTitle:@"Configuration Upgrade"
3158 buttons:[NSArray arrayWithObjects:
3159 @"Keep My Old Copy",
3160 @"Accept The New Copy",
3161 // XXX: @"See What Changed",
3163 defaultButtonIndex:0
3168 [sheet setBodyText:[NSString stringWithFormat:
3169 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3172 [sheet popupAlertAnimated:YES];
3175 - (void) _setProgressTitle:(NSString *)title {
3176 [status_ setText:title];
3179 - (void) _setProgressPercent:(NSNumber *)percent {
3180 [progress_ setProgress:[percent floatValue]];
3183 - (void) _addProgressOutput:(NSString *)output {
3184 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3185 CGSize size = [output_ contentSize];
3186 CGRect rect = {{0, size.height}, {size.width, 0}};
3187 [output_ scrollRectToVisible:rect animated:YES];
3190 - (BOOL) isRunning {
3197 /* Package Cell {{{ */
3198 @interface PackageCell : UISimpleTableCell {
3201 NSString *description_;
3205 UITextLabel *status_;
3209 - (PackageCell *) init;
3210 - (void) setPackage:(Package *)package;
3212 + (int) heightForPackage:(Package *)package;
3216 @implementation PackageCell
3218 - (void) clearPackage {
3229 if (description_ != nil) {
3230 [description_ release];
3234 if (source_ != nil) {
3239 if (badge_ != nil) {
3246 [self clearPackage];
3253 - (PackageCell *) init {
3254 if ((self = [super init]) != nil) {
3256 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3257 [status_ setBackgroundColor:[UIColor clearColor]];
3258 [status_ setFont:small];
3263 - (void) setPackage:(Package *)package {
3264 [self clearPackage];
3266 Source *source = [package source];
3267 NSString *section = [package section];
3269 section = Simplify(section);
3271 icon_ = [[package icon] retain];
3273 name_ = [[package name] retain];
3274 description_ = [[package tagline] retain];
3276 NSString *label = nil;
3277 bool trusted = false;
3279 if (source != nil) {
3280 label = [source label];
3281 trusted = [source trusted];
3282 } else if ([[package id] isEqualToString:@"firmware"])
3285 label = @"Unknown/Local";
3287 NSString *from = [NSString stringWithFormat:@"from %@", label];
3289 if (section != nil && ![section isEqualToString:label])
3290 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3292 source_ = [from retain];
3294 if (NSString *purpose = [package primaryPurpose])
3295 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3296 badge_ = [badge_ retain];
3299 if (NSString *mode = [package mode]) {
3300 [badge_ setImage:[UIImage applicationImageNamed:
3301 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3304 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3305 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3306 } else if ([package half]) {
3307 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3308 [status_ setText:@"Package Damaged"];
3309 [status_ setColor:[UIColor redColor]];
3311 [badge_ setImage:nil];
3312 [status_ setText:nil];
3317 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3320 rect.size = [icon_ size];
3322 rect.size.width /= 2;
3323 rect.size.height /= 2;
3325 rect.origin.x = 25 - rect.size.width / 2;
3326 rect.origin.y = 25 - rect.size.height / 2;
3328 [icon_ drawInRect:rect];
3331 if (badge_ != nil) {
3332 CGSize size = [badge_ size];
3334 [badge_ drawAtPoint:CGPointMake(
3335 36 - size.width / 2,
3336 36 - size.height / 2
3345 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3346 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3350 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3352 [super drawContentInRect:rect selected:selected];
3355 + (int) heightForPackage:(Package *)package {
3356 NSString *tagline([package tagline]);
3357 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3359 if ([package hasMode] || [package half])
3368 /* Section Cell {{{ */
3369 @interface SectionCell : UISimpleTableCell {
3374 _UISwitchSlider *switch_;
3379 - (void) setSection:(Section *)section editing:(BOOL)editing;
3383 @implementation SectionCell
3385 - (void) clearSection {
3386 if (section_ != nil) {
3396 if (count_ != nil) {
3403 [self clearSection];
3410 if ((self = [super init]) != nil) {
3411 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3413 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3414 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3418 - (void) onSwitch:(id)sender {
3419 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3420 if (metadata == nil) {
3421 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3422 [Sections_ setObject:metadata forKey:section_];
3426 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3429 - (void) setSection:(Section *)section editing:(BOOL)editing {
3430 if (editing != editing_) {
3432 [switch_ removeFromSuperview];
3434 [self addSubview:switch_];
3438 [self clearSection];
3440 if (section == nil) {
3441 name_ = [@"All Packages" retain];
3444 section_ = [section name];
3445 if (section_ != nil)
3446 section_ = [section_ retain];
3447 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3448 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3451 [switch_ setValue:isSectionVisible(section_) animated:NO];
3455 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3456 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3463 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3465 CGSize size = [count_ sizeWithFont:Font14_];
3469 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3471 [super drawContentInRect:rect selected:selected];
3477 /* File Table {{{ */
3478 @interface FileTable : RVPage {
3479 _transient Database *database_;
3482 NSMutableArray *files_;
3486 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3487 - (void) setPackage:(Package *)package;
3491 @implementation FileTable
3494 if (package_ != nil)
3503 - (int) numberOfRowsInTable:(UITable *)table {
3504 return files_ == nil ? 0 : [files_ count];
3507 - (float) table:(UITable *)table heightForRow:(int)row {
3511 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3512 if (reusing == nil) {
3513 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3514 UIFont *font = [UIFont systemFontOfSize:16];
3515 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3517 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3521 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3525 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3526 if ((self = [super initWithBook:book]) != nil) {
3527 database_ = database;
3529 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3531 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3532 [self addSubview:list_];
3534 UITableColumn *column = [[[UITableColumn alloc]
3535 initWithTitle:@"Name"
3537 width:[self frame].size.width
3540 [list_ setDataSource:self];
3541 [list_ setSeparatorStyle:1];
3542 [list_ addTableColumn:column];
3543 [list_ setDelegate:self];
3544 [list_ setReusesTableCells:YES];
3548 - (void) setPackage:(Package *)package {
3549 if (package_ != nil) {
3550 [package_ autorelease];
3559 [files_ removeAllObjects];
3561 if (package != nil) {
3562 package_ = [package retain];
3563 name_ = [[package id] retain];
3565 if (NSArray *files = [package files])
3566 [files_ addObjectsFromArray:files];
3568 if ([files_ count] != 0) {
3569 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3570 [files_ removeObjectAtIndex:0];
3571 [files_ sortUsingSelector:@selector(compareByPath:)];
3573 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3574 [stack addObject:@"/"];
3576 for (int i(0), e([files_ count]); i != e; ++i) {
3577 NSString *file = [files_ objectAtIndex:i];
3578 while (![file hasPrefix:[stack lastObject]])
3579 [stack removeLastObject];
3580 NSString *directory = [stack lastObject];
3581 [stack addObject:[file stringByAppendingString:@"/"]];
3582 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3583 ([stack count] - 2) * 3, "",
3584 [file substringFromIndex:[directory length]]
3593 - (void) resetViewAnimated:(BOOL)animated {
3594 [list_ resetViewAnimated:animated];
3597 - (void) reloadData {
3598 [self setPackage:[database_ packageWithName:name_]];
3599 [self reloadButtons];
3602 - (NSString *) title {
3603 return @"Installed Files";
3606 - (NSString *) backButtonTitle {
3612 /* Package View {{{ */
3613 @interface PackageView : BrowserView {
3614 _transient Database *database_;
3617 NSMutableArray *buttons_;
3620 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3621 - (void) setPackage:(Package *)package;
3625 @implementation PackageView
3628 if (package_ != nil)
3636 - (void) _clickButtonWithName:(NSString *)name {
3637 if ([name isEqualToString:@"Install"])
3638 [delegate_ installPackage:package_];
3639 else if ([name isEqualToString:@"Reinstall"])
3640 [delegate_ installPackage:package_];
3641 else if ([name isEqualToString:@"Remove"])
3642 [delegate_ removePackage:package_];
3643 else if ([name isEqualToString:@"Upgrade"])
3644 [delegate_ installPackage:package_];
3645 else _assert(false);
3648 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3649 int count = [buttons_ count];
3650 _assert(count != 0);
3651 _assert(button <= count + 1);
3653 if (count != button - 1)
3654 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3659 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3660 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3661 return [super webView:sender didFinishLoadForFrame:frame];
3664 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3665 [window setValue:package_ forKey:@"package"];
3666 [super webView:sender didClearWindowObject:window forFrame:frame];
3670 - (void) _rightButtonClicked {
3671 /*[super _rightButtonClicked];
3674 int count = [buttons_ count];
3675 _assert(count != 0);
3678 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3680 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3681 [buttons addObjectsFromArray:buttons_];
3682 [buttons addObject:@"Cancel"];
3684 [delegate_ slideUp:[[[UIActionSheet alloc]
3687 defaultButtonIndex:2
3695 - (NSString *) _rightButtonTitle {
3696 int count = [buttons_ count];
3697 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3700 - (NSString *) backButtonTitle {
3704 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3705 if ((self = [super initWithBook:book]) != nil) {
3706 database_ = database;
3707 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3711 - (void) setPackage:(Package *)package {
3712 if (package_ != nil) {
3713 [package_ autorelease];
3722 [buttons_ removeAllObjects];
3724 if (package != nil) {
3725 package_ = [package retain];
3726 name_ = [[package id] retain];
3728 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3730 if ([package_ source] == nil);
3731 else if ([package_ upgradableAndEssential:NO])
3732 [buttons_ addObject:@"Upgrade"];
3733 else if ([package_ installed] == nil)
3734 [buttons_ addObject:@"Install"];
3736 [buttons_ addObject:@"Reinstall"];
3737 if ([package_ installed] != nil)
3738 [buttons_ addObject:@"Remove"];
3742 - (void) reloadData {
3743 [self setPackage:[database_ packageWithName:name_]];
3744 [self reloadButtons];
3749 /* Package Table {{{ */
3750 @interface PackageTable : RVPage {
3751 _transient Database *database_;
3755 NSMutableArray *packages_;
3756 NSMutableArray *sections_;
3757 UISectionList *list_;
3760 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3762 - (void) setDelegate:(id)delegate;
3763 - (void) setObject:(id)object;
3765 - (void) reloadData;
3766 - (void) resetCursor;
3768 - (UISectionList *) list;
3770 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3774 @implementation PackageTable
3777 [list_ setDataSource:nil];
3782 [packages_ release];
3783 [sections_ release];
3788 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3789 return [sections_ count];
3792 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3793 return [[sections_ objectAtIndex:section] name];
3796 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3797 return [[sections_ objectAtIndex:section] row];
3800 - (int) numberOfRowsInTable:(UITable *)table {
3801 return [packages_ count];
3804 - (float) table:(UITable *)table heightForRow:(int)row {
3805 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3808 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3810 reusing = [[[PackageCell alloc] init] autorelease];
3811 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3815 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3819 - (void) tableRowSelected:(NSNotification *)notification {
3820 int row = [[notification object] selectedRow];
3824 Package *package = [packages_ objectAtIndex:row];
3825 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3826 [view setDelegate:delegate_];
3827 [view setPackage:package];
3828 [book_ pushPage:view];
3831 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3832 if ((self = [super initWithBook:book]) != nil) {
3833 database_ = database;
3834 title_ = [title retain];
3836 object_ = object == nil ? nil : [object retain];
3838 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3839 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3841 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3842 [list_ setDataSource:self];
3844 UITableColumn *column = [[[UITableColumn alloc]
3845 initWithTitle:@"Name"
3847 width:[self frame].size.width
3850 UITable *table = [list_ table];
3851 [table setSeparatorStyle:1];
3852 [table addTableColumn:column];
3853 [table setDelegate:self];
3854 [table setReusesTableCells:YES];
3856 [self addSubview:list_];
3859 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3860 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3864 - (void) setDelegate:(id)delegate {
3865 delegate_ = delegate;
3868 - (void) setObject:(id)object {
3874 object_ = [object retain];
3877 - (void) reloadData {
3878 NSArray *packages = [database_ packages];
3880 [packages_ removeAllObjects];
3881 [sections_ removeAllObjects];
3883 for (size_t i(0); i != [packages count]; ++i) {
3884 Package *package([packages objectAtIndex:i]);
3885 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3886 [packages_ addObject:package];
3889 Section *section = nil;
3891 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3892 Package *package = [packages_ objectAtIndex:offset];
3893 NSString *name = [package index];
3895 if (section == nil || ![[section name] isEqualToString:name]) {
3896 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3897 [sections_ addObject:section];
3900 [section addToCount];
3906 - (NSString *) title {
3910 - (void) resetViewAnimated:(BOOL)animated {
3911 [list_ resetViewAnimated:animated];
3914 - (void) resetCursor {
3915 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3918 - (UISectionList *) list {
3922 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3923 [list_ setShouldHideHeaderInShortLists:hide];
3929 /* Add Source View {{{ */
3930 @interface AddSourceView : RVPage {
3931 _transient Database *database_;
3934 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3938 @implementation AddSourceView
3940 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3941 if ((self = [super initWithBook:book]) != nil) {
3942 database_ = database;
3948 /* Source Cell {{{ */
3949 @interface SourceCell : UITableCell {
3952 NSString *description_;
3958 - (SourceCell *) initWithSource:(Source *)source;
3962 @implementation SourceCell
3967 [description_ release];
3972 - (SourceCell *) initWithSource:(Source *)source {
3973 if ((self = [super init]) != nil) {
3975 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3977 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3978 icon_ = [icon_ retain];
3980 origin_ = [[source name] retain];
3981 label_ = [[source uri] retain];
3982 description_ = [[source description] retain];
3986 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3988 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3995 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3999 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4003 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4005 [super drawContentInRect:rect selected:selected];
4010 /* Source Table {{{ */
4011 @interface SourceTable : RVPage {
4012 _transient Database *database_;
4013 UISectionList *list_;
4014 NSMutableArray *sources_;
4015 UIActionSheet *alert_;
4019 UIProgressHUD *hud_;
4022 //NSURLConnection *installer_;
4023 NSURLConnection *trivial_bz2_;
4024 NSURLConnection *trivial_gz_;
4025 //NSURLConnection *automatic_;
4030 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4034 @implementation SourceTable
4036 - (void) _deallocConnection:(NSURLConnection *)connection {
4037 if (connection != nil) {
4038 [connection cancel];
4039 //[connection setDelegate:nil];
4040 [connection release];
4045 [[list_ table] setDelegate:nil];
4046 [list_ setDataSource:nil];
4055 //[self _deallocConnection:installer_];
4056 [self _deallocConnection:trivial_gz_];
4057 [self _deallocConnection:trivial_bz2_];
4058 //[self _deallocConnection:automatic_];
4065 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4066 return offset_ == 0 ? 1 : 2;
4069 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4070 switch (section + (offset_ == 0 ? 1 : 0)) {
4071 case 0: return @"Entered by User";
4072 case 1: return @"Installed by Packages";
4080 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4081 switch (section + (offset_ == 0 ? 1 : 0)) {
4083 case 1: return offset_;
4091 - (int) numberOfRowsInTable:(UITable *)table {
4092 return [sources_ count];
4095 - (float) table:(UITable *)table heightForRow:(int)row {
4096 Source *source = [sources_ objectAtIndex:row];
4097 return [source description] == nil ? 56 : 73;
4100 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4101 Source *source = [sources_ objectAtIndex:row];
4102 // XXX: weird warning, stupid selectors ;P
4103 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4106 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4110 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4114 - (void) tableRowSelected:(NSNotification*)notification {
4115 UITable *table([list_ table]);
4116 int row([table selectedRow]);
4120 Source *source = [sources_ objectAtIndex:row];
4122 PackageTable *packages = [[[PackageTable alloc]
4125 title:[source label]
4126 filter:@selector(isVisibleInSource:)
4130 [packages setDelegate:delegate_];
4132 [book_ pushPage:packages];
4135 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4136 Source *source = [sources_ objectAtIndex:row];
4137 return [source record] != nil;
4140 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4141 [[list_ table] setDeleteConfirmationRow:row];
4144 - (void) table:(UITable *)table deleteRow:(int)row {
4145 Source *source = [sources_ objectAtIndex:row];
4146 [Sources_ removeObjectForKey:[source key]];
4147 [delegate_ syncData];
4150 - (void) _endConnection:(NSURLConnection *)connection {
4151 NSURLConnection **field = NULL;
4152 if (connection == trivial_bz2_)
4153 field = &trivial_bz2_;
4154 else if (connection == trivial_gz_)
4155 field = &trivial_gz_;
4156 _assert(field != NULL);
4157 [connection release];
4161 trivial_bz2_ == nil &&
4164 [delegate_ setStatusBarShowsProgress:NO];
4167 [hud_ removeFromSuperview];
4172 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4175 @"./", @"Distribution",
4176 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4178 [delegate_ syncData];
4179 } else if (error_ != nil) {
4180 UIActionSheet *sheet = [[[UIActionSheet alloc]
4181 initWithTitle:@"Verification Error"
4182 buttons:[NSArray arrayWithObjects:@"OK", nil]
4183 defaultButtonIndex:0
4188 [sheet setBodyText:[error_ localizedDescription]];
4189 [sheet popupAlertAnimated:YES];
4191 UIActionSheet *sheet = [[[UIActionSheet alloc]
4192 initWithTitle:@"Did not Find Repository"
4193 buttons:[NSArray arrayWithObjects:@"OK", nil]
4194 defaultButtonIndex:0
4199 [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."];
4200 [sheet popupAlertAnimated:YES];
4206 if (error_ != nil) {
4213 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4214 switch ([response statusCode]) {
4220 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4221 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4223 error_ = [error retain];
4224 [self _endConnection:connection];
4227 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4228 [self _endConnection:connection];
4231 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4232 NSMutableURLRequest *request = [NSMutableURLRequest
4233 requestWithURL:[NSURL URLWithString:href]
4234 cachePolicy:NSURLRequestUseProtocolCachePolicy
4235 timeoutInterval:20.0
4238 [request setHTTPMethod:method];
4240 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4243 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4244 NSString *context = [sheet context];
4245 if ([context isEqualToString:@"source"])
4248 NSString *href = [[sheet textField] text];
4250 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4252 if (![href hasSuffix:@"/"])
4253 href_ = [href stringByAppendingString:@"/"];
4256 href_ = [href_ retain];
4258 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4259 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4260 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4264 hud_ = [delegate_ addProgressHUD];
4265 [hud_ setText:@"Verifying URL"];
4278 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4279 if ((self = [super initWithBook:book]) != nil) {
4280 database_ = database;
4281 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4283 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4284 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4285 [list_ setShouldHideHeaderInShortLists:NO];
4287 [self addSubview:list_];
4288 [list_ setDataSource:self];
4290 UITableColumn *column = [[UITableColumn alloc]
4291 initWithTitle:@"Name"
4293 width:[self frame].size.width
4296 UITable *table = [list_ table];
4297 [table setSeparatorStyle:1];
4298 [table addTableColumn:column];
4299 [table setDelegate:self];
4303 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4304 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4308 - (void) reloadData {
4310 _assert(list.ReadMainList());
4312 [sources_ removeAllObjects];
4313 [sources_ addObjectsFromArray:[database_ sources]];
4314 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4316 int count = [sources_ count];
4317 for (offset_ = 0; offset_ != count; ++offset_) {
4318 Source *source = [sources_ objectAtIndex:offset_];
4319 if ([source record] == nil)
4326 - (void) resetViewAnimated:(BOOL)animated {
4327 [list_ resetViewAnimated:animated];
4330 - (void) _leftButtonClicked {
4331 /*[book_ pushPage:[[[AddSourceView alloc]
4336 UIActionSheet *sheet = [[[UIActionSheet alloc]
4337 initWithTitle:@"Enter Cydia/APT URL"
4338 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4339 defaultButtonIndex:0
4344 [sheet addTextFieldWithValue:@"http://" label:@""];
4346 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4347 [traits setAutocapitalizationType:0];
4348 [traits setKeyboardType:3];
4349 [traits setAutocorrectionType:1];
4351 [sheet popupAlertAnimated:YES];
4354 - (void) _rightButtonClicked {
4355 UITable *table = [list_ table];
4356 BOOL editing = [table isRowDeletionEnabled];
4357 [table enableRowDeletion:!editing animated:YES];
4358 [book_ reloadButtonsForPage:self];
4361 - (NSString *) title {
4365 - (NSString *) leftButtonTitle {
4366 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4369 - (NSString *) rightButtonTitle {
4370 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4373 - (UINavigationButtonStyle) rightButtonStyle {
4374 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4380 /* Installed View {{{ */
4381 @interface InstalledView : RVPage {
4382 _transient Database *database_;
4383 PackageTable *packages_;
4387 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4391 @implementation InstalledView
4394 [packages_ release];
4398 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4399 if ((self = [super initWithBook:book]) != nil) {
4400 database_ = database;
4402 packages_ = [[PackageTable alloc]
4406 filter:@selector(isInstalledAndVisible:)
4407 with:[NSNumber numberWithBool:YES]
4410 [self addSubview:packages_];
4412 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4413 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4417 - (void) resetViewAnimated:(BOOL)animated {
4418 [packages_ resetViewAnimated:animated];
4421 - (void) reloadData {
4422 [packages_ reloadData];
4425 - (void) _rightButtonClicked {
4426 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4427 [packages_ reloadData];
4429 [book_ reloadButtonsForPage:self];
4432 - (NSString *) title {
4433 return @"Installed";
4436 - (NSString *) backButtonTitle {
4440 - (NSString *) rightButtonTitle {
4441 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4444 - (UINavigationButtonStyle) rightButtonStyle {
4445 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4448 - (void) setDelegate:(id)delegate {
4449 [super setDelegate:delegate];
4450 [packages_ setDelegate:delegate];
4457 @interface HomeView : BrowserView {
4462 @implementation HomeView
4464 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4468 - (void) _leftButtonClicked {
4469 UIActionSheet *sheet = [[[UIActionSheet alloc]
4470 initWithTitle:@"About Cydia Installer"
4471 buttons:[NSArray arrayWithObjects:@"Close", nil]
4472 defaultButtonIndex:0
4478 @"Copyright (C) 2008\n"
4479 "Jay Freeman (saurik)\n"
4480 "saurik@saurik.com\n"
4481 "http://www.saurik.com/\n"
4484 "http://www.theokorigroup.com/\n"
4486 "College of Creative Studies,\n"
4487 "University of California,\n"
4489 "http://www.ccs.ucsb.edu/"
4492 [sheet popupAlertAnimated:YES];
4495 - (NSString *) leftButtonTitle {
4501 /* Manage View {{{ */
4502 @interface ManageView : BrowserView {
4507 @implementation ManageView
4509 - (NSString *) title {
4513 - (void) _leftButtonClicked {
4514 [delegate_ askForSettings];
4517 - (NSString *) leftButtonTitle {
4521 - (NSString *) _rightButtonTitle {
4528 @interface WebView (Cydia)
4529 - (void) setScriptDebugDelegate:(id)delegate;
4530 - (void) _setFormDelegate:(id)delegate;
4531 - (void) _setUIKitDelegate:(id)delegate;
4532 - (void) setWebMailDelegate:(id)delegate;
4533 - (void) _setLayoutInterval:(float)interval;
4536 /* Indirect Delegate {{{ */
4537 @interface IndirectDelegate : NSProxy {
4538 _transient volatile id delegate_;
4541 - (void) setDelegate:(id)delegate;
4542 - (id) initWithDelegate:(id)delegate;
4545 @implementation IndirectDelegate
4547 - (void) setDelegate:(id)delegate {
4548 delegate_ = delegate;
4551 - (id) initWithDelegate:(id)delegate {
4552 delegate_ = delegate;
4556 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4557 if (delegate_ != nil)
4558 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4560 // XXX: I fucking hate Apple so very very bad
4561 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4564 - (void) forwardInvocation:(NSInvocation *)inv {
4565 SEL sel = [inv selector];
4566 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4567 [inv invokeWithTarget:delegate_];
4572 /* Browser Implementation {{{ */
4573 @implementation BrowserView
4576 WebView *webview = [webview_ webView];
4577 [webview setFrameLoadDelegate:nil];
4578 [webview setResourceLoadDelegate:nil];
4579 [webview setUIDelegate:nil];
4580 [webview setScriptDebugDelegate:nil];
4581 [webview setPolicyDelegate:nil];
4583 [webview setDownloadDelegate:nil];
4585 [webview _setFormDelegate:nil];
4586 [webview _setUIKitDelegate:nil];
4587 [webview setWebMailDelegate:nil];
4588 [webview setEditingDelegate:nil];
4590 [webview_ setDelegate:nil];
4591 [webview_ setGestureDelegate:nil];
4593 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4598 [webview_ removeFromSuperview];
4599 [Documents_ addObject:[webview_ autorelease]];
4604 [indirect_ setDelegate:nil];
4605 [indirect_ release];
4607 [scroller_ setDelegate:nil];
4609 [scroller_ release];
4611 [indicator_ release];
4617 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4618 [self loadRequest:[NSURLRequest
4621 timeoutInterval:30.0
4625 - (void) loadURL:(NSURL *)url {
4626 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4629 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4630 NSMutableURLRequest *copy = [request mutableCopy];
4632 if (Machine_ != NULL)
4633 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4634 if (UniqueID_ != nil)
4635 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4638 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4643 - (void) loadRequest:(NSURLRequest *)request {
4645 [webview_ loadRequest:request];
4648 - (void) reloadURL {
4649 if ([urls_ count] == 0)
4651 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4652 [urls_ removeLastObject];
4653 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4656 - (WebView *) webView {
4657 return [webview_ webView];
4660 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4661 [scroller_ setContentSize:frame.size];
4664 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4665 [self view:sender didSetFrame:frame];
4668 - (void) pushPage:(RVPage *)page {
4669 [self setBackButtonTitle:title_];
4670 [page setDelegate:delegate_];
4671 [book_ pushPage:page];
4674 - (BOOL) getSpecial:(NSURL *)url {
4675 NSString *href([url absoluteString]);
4676 NSString *scheme([[url scheme] lowercaseString]);
4680 if ([href hasPrefix:@"apptapp://package/"])
4681 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4682 else if ([scheme isEqualToString:@"cydia"]) {
4683 page = [delegate_ pageForURL:url hasTag:NULL];
4686 } else if (![scheme isEqualToString:@"apptapp"])
4690 [self pushPage:page];
4694 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4695 [window setValue:delegate_ forKey:@"cydia"];
4698 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4699 if (NSURL *url = [request URL]) {
4700 if (![self getSpecial:url]) {
4701 NSString *scheme([[url scheme] lowercaseString]);
4702 if ([scheme isEqualToString:@"mailto"])
4703 [delegate_ openMailToURL:url];
4712 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4713 NSURL *url([request URL]);
4715 if (url == nil) use: {
4720 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4723 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4724 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4727 [UIApp openURL:url];
4733 int store(_not(int));
4734 if (NSURL *itms = [url itmsURL:&store]) {
4736 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4737 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4744 NSString *scheme([[url scheme] lowercaseString]);
4746 if ([scheme isEqualToString:@"tel"]) {
4747 // XXX: intelligence
4751 if ([scheme isEqualToString:@"mailto"]) {
4752 [delegate_ openMailToURL:url];
4756 if ([self getSpecial:url])
4758 else if ([WebView _canHandleRequest:request])
4760 else if ([url isSpringboardHandledURL])
4766 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4767 //lprintf("Status:%s\n", [text UTF8String]);
4770 - (void) _pushPage {
4774 [book_ pushPage:self];
4777 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4778 NSURL *url = [request URL];
4779 if ([self getSpecial:url])
4782 return [self _addHeadersToRequest:request];
4785 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4786 [self setBackButtonTitle:title_];
4788 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4789 [browser setDelegate:delegate_];
4792 [browser loadRequest:[self _addHeadersToRequest:request]];
4793 [book_ pushPage:browser];
4796 return [browser webView];
4799 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4800 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4803 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4804 return [self _createWebViewWithRequest:request pushed:YES];
4807 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4808 if ([frame parentFrame] != nil)
4811 title_ = [title retain];
4812 [book_ reloadTitleForPage:self];
4815 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4816 if ([frame parentFrame] != nil)
4821 [indicator_ startAnimation];
4822 [self reloadButtons];
4824 if (title_ != nil) {
4829 [book_ reloadTitleForPage:self];
4831 WebView *webview = [webview_ webView];
4832 NSString *href = [webview mainFrameURL];
4833 [urls_ addObject:[NSURL URLWithString:href]];
4835 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4837 CGRect webrect = [scroller_ bounds];
4838 webrect.size.height = 0;
4839 [webview_ setFrame:webrect];
4842 - (void) _finishLoading {
4845 [indicator_ stopAnimation];
4846 [self reloadButtons];
4850 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4851 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4854 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4855 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4858 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4859 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4862 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4863 return [webview_ webView:sender didCommitLoadForFrame:frame];
4866 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4867 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4870 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4871 if ([frame parentFrame] == nil)
4872 [self _finishLoading];
4873 return [webview_ webView:sender didFinishLoadForFrame:frame];
4876 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4877 if ([frame parentFrame] != nil)
4879 [self _finishLoading];
4881 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4882 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4883 [[error localizedDescription] stringByAddingPercentEscapes]
4887 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
4889 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
4893 - (id) initWithBook:(RVBook *)book {
4894 if ((self = [super initWithBook:book]) != nil) {
4897 struct CGRect bounds = [self bounds];
4899 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4900 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4901 [self addSubview:pinstripe];
4903 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4904 [self addSubview:scroller_];
4906 [scroller_ setScrollingEnabled:YES];
4907 [scroller_ setAdjustForContentSizeChange:YES];
4908 [scroller_ setClipsSubviews:YES];
4909 [scroller_ setAllowsRubberBanding:YES];
4910 [scroller_ setScrollDecelerationFactor:0.99];
4911 [scroller_ setDelegate:self];
4913 CGRect webrect = [scroller_ bounds];
4914 webrect.size.height = 0;
4919 webview_ = [Documents_ lastObject];
4920 if (webview_ != nil) {
4921 webview_ = [webview_ retain];
4922 webview = [webview_ webView];
4923 [Documents_ removeLastObject];
4924 [webview_ setFrame:webrect];
4929 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
4930 webview = [webview_ webView];
4932 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4934 [webview_ setAllowsMessaging:YES];
4936 [webview_ setTilingEnabled:YES];
4937 [webview_ setDrawsGrid:NO];
4938 [webview_ setLogsTilingChanges:NO];
4939 [webview_ setTileMinificationFilter:kCAFilterNearest];
4940 [webview_ setDetectsPhoneNumbers:NO];
4941 [webview_ setAutoresizes:YES];
4943 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4944 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4945 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4947 [webview_ _setDocumentType:0x4];
4949 [webview_ setZoomsFocusedFormControl:YES];
4950 [webview_ setContentsPosition:7];
4951 [webview_ setEnabledGestures:0xa];
4952 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4953 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4955 [webview_ setSmoothsFonts:YES];
4957 [webview _setUsesLoaderCache:YES];
4958 [webview setGroupName:@"Cydia"];
4959 //[webview _setLayoutInterval:0.5];
4962 [webview_ setDelegate:self];
4963 [webview_ setGestureDelegate:self];
4964 [scroller_ addSubview:webview_];
4966 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4968 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
4969 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
4970 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
4972 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
4973 NSString *application = package == nil ? @"Cydia" : [NSString
4974 stringWithFormat:@"Cydia/%@",
4976 ]; [webview setApplicationNameForUserAgent:application];
4978 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
4980 [webview setFrameLoadDelegate:self];
4981 [webview setResourceLoadDelegate:indirect_];
4982 [webview setUIDelegate:self];
4983 [webview setScriptDebugDelegate:self];
4984 [webview setPolicyDelegate:self];
4986 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4988 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4989 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4990 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4994 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4995 [webview_ redrawScaledDocument];
4998 - (void) _rightButtonClicked {
5003 - (NSString *) _rightButtonTitle {
5007 - (NSString *) rightButtonTitle {
5008 return loading_ ? @"" : [self _rightButtonTitle];
5011 - (NSString *) title {
5012 return title_ == nil ? @"Loading" : title_;
5015 - (NSString *) backButtonTitle {
5019 - (void) setPageActive:(BOOL)active {
5021 [indicator_ removeFromSuperview];
5023 [[book_ navigationBar] addSubview:indicator_];
5026 - (void) resetViewAnimated:(BOOL)animated {
5029 - (void) setPushed:(bool)pushed {
5036 @interface CYBook : RVBook <
5039 _transient Database *database_;
5040 UINavigationBar *overlay_;
5041 UINavigationBar *underlay_;
5042 UIProgressIndicator *indicator_;
5043 UITextLabel *prompt_;
5044 UIProgressBar *progress_;
5045 UINavigationButton *cancel_;
5048 NSTimeInterval last_;
5051 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5057 /* Install View {{{ */
5058 @interface InstallView : RVPage {
5059 _transient Database *database_;
5060 NSMutableArray *sections_;
5061 NSMutableArray *filtered_;
5062 UITransitionView *transition_;
5068 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5069 - (void) reloadData;
5074 @implementation InstallView
5077 [list_ setDataSource:nil];
5078 [list_ setDelegate:nil];
5080 [sections_ release];
5081 [filtered_ release];
5082 [transition_ release];
5084 [accessory_ release];
5088 - (int) numberOfRowsInTable:(UITable *)table {
5089 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5092 - (float) table:(UITable *)table heightForRow:(int)row {
5096 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5098 reusing = [[[SectionCell alloc] init] autorelease];
5099 [(SectionCell *)reusing setSection:(editing_ ?
5100 [sections_ objectAtIndex:row] :
5101 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5102 ) editing:editing_];
5106 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5110 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5114 - (void) tableRowSelected:(NSNotification *)notification {
5115 int row = [[notification object] selectedRow];
5126 title = @"All Packages";
5128 section = [filtered_ objectAtIndex:(row - 1)];
5129 name = [section name];
5135 title = @"(No Section)";
5139 PackageTable *table = [[[PackageTable alloc]
5143 filter:@selector(isVisiblyUninstalledInSection:)
5147 [table setDelegate:delegate_];
5149 [book_ pushPage:table];
5152 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5153 if ((self = [super initWithBook:book]) != nil) {
5154 database_ = database;
5156 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5157 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5159 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5160 [self addSubview:transition_];
5162 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5163 [transition_ transition:0 toView:list_];
5165 UITableColumn *column = [[[UITableColumn alloc]
5166 initWithTitle:@"Name"
5168 width:[self frame].size.width
5171 [list_ setDataSource:self];
5172 [list_ setSeparatorStyle:1];
5173 [list_ addTableColumn:column];
5174 [list_ setDelegate:self];
5175 [list_ setReusesTableCells:YES];
5179 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5180 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5184 - (void) reloadData {
5185 NSArray *packages = [database_ packages];
5187 [sections_ removeAllObjects];
5188 [filtered_ removeAllObjects];
5190 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5191 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5193 for (size_t i(0); i != [packages count]; ++i) {
5194 Package *package([packages objectAtIndex:i]);
5195 NSString *name([package section]);
5198 Section *section([sections objectForKey:name]);
5199 if (section == nil) {
5200 section = [[[Section alloc] initWithName:name] autorelease];
5201 [sections setObject:section forKey:name];
5205 if ([package valid] && [package installed] == nil && [package visible])
5206 [filtered addObject:package];
5209 [sections_ addObjectsFromArray:[sections allValues]];
5210 [sections_ sortUsingSelector:@selector(compareByName:)];
5212 [filtered sortUsingSelector:@selector(compareBySection:)];
5214 Section *section = nil;
5215 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5216 Package *package = [filtered objectAtIndex:offset];
5217 NSString *name = [package section];
5219 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5220 section = name == nil ?
5221 [[[Section alloc] initWithName:nil] autorelease] :
5222 [sections objectForKey:name];
5223 [filtered_ addObject:section];
5226 [section addToCount];
5232 - (void) resetView {
5234 [self _rightButtonClicked];
5237 - (void) resetViewAnimated:(BOOL)animated {
5238 [list_ resetViewAnimated:animated];
5241 - (void) _rightButtonClicked {
5242 if ((editing_ = !editing_))
5245 [delegate_ updateData];
5248 [book_ reloadTitleForPage:self];
5249 [book_ reloadButtonsForPage:self];
5252 - (NSString *) title {
5253 return editing_ ? @"Section Visibility" : @"Install by Section";
5256 - (NSString *) backButtonTitle {
5260 - (NSString *) rightButtonTitle {
5261 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5264 - (UINavigationButtonStyle) rightButtonStyle {
5265 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5268 - (UIView *) accessoryView {
5274 /* Changes View {{{ */
5275 @interface ChangesView : RVPage {
5276 _transient Database *database_;
5277 NSMutableArray *packages_;
5278 NSMutableArray *sections_;
5279 UISectionList *list_;
5283 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5284 - (void) reloadData;
5288 @implementation ChangesView
5291 [[list_ table] setDelegate:nil];
5292 [list_ setDataSource:nil];
5294 [packages_ release];
5295 [sections_ release];
5300 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5301 return [sections_ count];
5304 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5305 return [[sections_ objectAtIndex:section] name];
5308 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5309 return [[sections_ objectAtIndex:section] row];
5312 - (int) numberOfRowsInTable:(UITable *)table {
5313 return [packages_ count];
5316 - (float) table:(UITable *)table heightForRow:(int)row {
5317 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5320 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5322 reusing = [[[PackageCell alloc] init] autorelease];
5323 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5327 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5331 - (void) tableRowSelected:(NSNotification *)notification {
5332 int row = [[notification object] selectedRow];
5335 Package *package = [packages_ objectAtIndex:row];
5336 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5337 [view setDelegate:delegate_];
5338 [view setPackage:package];
5339 [book_ pushPage:view];
5342 - (void) _leftButtonClicked {
5343 [(CYBook *)book_ update];
5344 [self reloadButtons];
5347 - (void) _rightButtonClicked {
5348 [delegate_ distUpgrade];
5351 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5352 if ((self = [super initWithBook:book]) != nil) {
5353 database_ = database;
5355 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5356 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5358 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5359 [self addSubview:list_];
5361 [list_ setShouldHideHeaderInShortLists:NO];
5362 [list_ setDataSource:self];
5363 //[list_ setSectionListStyle:1];
5365 UITableColumn *column = [[[UITableColumn alloc]
5366 initWithTitle:@"Name"
5368 width:[self frame].size.width
5371 UITable *table = [list_ table];
5372 [table setSeparatorStyle:1];
5373 [table addTableColumn:column];
5374 [table setDelegate:self];
5375 [table setReusesTableCells:YES];
5379 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5380 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5384 - (void) reloadData {
5385 NSArray *packages = [database_ packages];
5387 [packages_ removeAllObjects];
5388 [sections_ removeAllObjects];
5390 for (size_t i(0); i != [packages count]; ++i) {
5391 Package *package([packages objectAtIndex:i]);
5394 [package installed] == nil && [package valid] && [package visible] ||
5395 [package upgradableAndEssential:NO]
5397 [packages_ addObject:package];
5400 [packages_ sortUsingSelector:@selector(compareForChanges:)];
5402 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5403 Section *section = nil;
5406 bool unseens = false;
5408 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5410 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5411 Package *package = [packages_ objectAtIndex:offset];
5413 if ([package upgradableAndEssential:YES]) {
5415 [upgradable addToCount];
5418 NSDate *seen = [package seen];
5423 name = [@"n/a ?" retain];
5425 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
5428 if (section == nil || ![[section name] isEqualToString:name]) {
5429 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5430 [sections_ addObject:section];
5434 [section addToCount];
5438 CFRelease(formatter);
5441 Section *last = [sections_ lastObject];
5442 size_t count = [last count];
5443 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5444 [sections_ removeLastObject];
5448 [sections_ insertObject:upgradable atIndex:0];
5451 [self reloadButtons];
5454 - (void) resetViewAnimated:(BOOL)animated {
5455 [list_ resetViewAnimated:animated];
5458 - (NSString *) leftButtonTitle {
5459 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5462 - (NSString *) rightButtonTitle {
5463 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5466 - (NSString *) title {
5472 /* Search View {{{ */
5473 @protocol SearchViewDelegate
5474 - (void) showKeyboard:(BOOL)show;
5477 @interface SearchView : RVPage {
5479 UISearchField *field_;
5480 UITransitionView *transition_;
5481 PackageTable *table_;
5482 UIPreferencesTable *advanced_;
5488 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5489 - (void) reloadData;
5493 @implementation SearchView
5496 [field_ setDelegate:nil];
5498 [accessory_ release];
5500 [transition_ release];
5502 [advanced_ release];
5507 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5511 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5513 case 0: return @"Advanced Search (Coming Soon!)";
5515 default: _assert(false);
5519 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5523 default: _assert(false);
5527 - (void) _showKeyboard:(BOOL)show {
5528 CGSize keysize = [UIKeyboard defaultSize];
5529 CGRect keydown = [book_ pageBounds];
5530 CGRect keyup = keydown;
5531 keyup.size.height -= keysize.height - ButtonBarHeight_;
5533 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5535 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5536 [animation setSignificantRectFields:8];
5539 [animation setStartFrame:keydown];
5540 [animation setEndFrame:keyup];
5542 [animation setStartFrame:keyup];
5543 [animation setEndFrame:keydown];
5546 UIAnimator *animator = [UIAnimator sharedAnimator];
5549 addAnimations:[NSArray arrayWithObjects:animation, nil]
5550 withDuration:(KeyboardTime_ - delay)
5555 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5557 [delegate_ showKeyboard:show];
5560 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5561 [self _showKeyboard:YES];
5564 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5565 [self _showKeyboard:NO];
5568 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5570 NSString *text([field_ text]);
5571 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5577 - (void) textFieldClearButtonPressed:(UITextField *)field {
5581 - (void) keyboardInputShouldDelete:(id)input {
5585 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5586 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5590 [field_ resignFirstResponder];
5595 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5596 if ((self = [super initWithBook:book]) != nil) {
5597 CGRect pageBounds = [book_ pageBounds];
5599 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5600 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5601 [self addSubview:pinstripe];*/
5603 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5604 [self addSubview:transition_];
5606 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5608 [advanced_ setReusesTableCells:YES];
5609 [advanced_ setDataSource:self];
5610 [advanced_ reloadData];
5612 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5613 CGColor dimmed(space_, 0, 0, 0, 0.5);
5614 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5616 table_ = [[PackageTable alloc]
5620 filter:@selector(isUnfilteredAndSearchedForBy:)
5624 [table_ setShouldHideHeaderInShortLists:NO];
5625 [transition_ transition:0 toView:table_];
5634 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5641 [self bounds].size.width - area.origin.x - 18;
5643 area.size.height = [UISearchField defaultHeight];
5645 field_ = [[UISearchField alloc] initWithFrame:area];
5647 UIFont *font = [UIFont systemFontOfSize:16];
5648 [field_ setFont:font];
5650 [field_ setPlaceholder:@"Package Names & Descriptions"];
5651 [field_ setDelegate:self];
5653 [field_ setPaddingTop:5];
5655 UITextInputTraits *traits = [field_ textInputTraits];
5656 [traits setAutocapitalizationType:0];
5657 [traits setAutocorrectionType:1];
5658 [traits setReturnKeyType:6];
5660 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5662 accessory_ = [[UIView alloc] initWithFrame:accrect];
5663 [accessory_ addSubview:field_];
5665 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5666 [configure setShowPressFeedback:YES];
5667 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5668 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5669 [accessory_ addSubview:configure];*/
5671 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5672 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5678 LKAnimation *animation = [LKTransition animation];
5679 [animation setType:@"oglFlip"];
5680 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5681 [animation setFillMode:@"extended"];
5682 [animation setTransitionFlags:3];
5683 [animation setDuration:10];
5684 [animation setSpeed:0.35];
5685 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5686 [[transition_ _layer] addAnimation:animation forKey:0];
5687 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5688 flipped_ = !flipped_;
5692 - (void) configurePushed {
5693 [field_ resignFirstResponder];
5697 - (void) resetViewAnimated:(BOOL)animated {
5700 [table_ resetViewAnimated:animated];
5703 - (void) _reloadData {
5706 - (void) reloadData {
5709 [table_ setObject:[field_ text]];
5710 [table_ reloadData];
5711 [table_ resetCursor];
5714 - (UIView *) accessoryView {
5718 - (NSString *) title {
5722 - (NSString *) backButtonTitle {
5726 - (void) setDelegate:(id)delegate {
5727 [table_ setDelegate:delegate];
5728 [super setDelegate:delegate];
5734 @implementation CYBook
5738 [indicator_ release];
5740 [progress_ release];
5745 - (NSString *) getTitleForPage:(RVPage *)page {
5746 return Simplify([super getTitleForPage:page]);
5754 [UIView beginAnimations:nil context:NULL];
5756 CGRect ovrframe = [overlay_ frame];
5757 ovrframe.origin.y = 0;
5758 [overlay_ setFrame:ovrframe];
5760 CGRect barframe = [navbar_ frame];
5761 barframe.origin.y += ovrframe.size.height;
5762 [navbar_ setFrame:barframe];
5764 CGRect trnframe = [transition_ frame];
5765 trnframe.origin.y += ovrframe.size.height;
5766 trnframe.size.height -= ovrframe.size.height;
5767 [transition_ setFrame:trnframe];
5769 [UIView endAnimations];
5771 [indicator_ startAnimation];
5772 [prompt_ setText:@"Updating Database"];
5773 [progress_ setProgress:0];
5776 last_ = [NSDate timeIntervalSinceReferenceDate];
5778 [overlay_ addSubview:cancel_];
5781 detachNewThreadSelector:@selector(_update)
5790 [indicator_ stopAnimation];
5792 [UIView beginAnimations:nil context:NULL];
5794 CGRect ovrframe = [overlay_ frame];
5795 ovrframe.origin.y = -ovrframe.size.height;
5796 [overlay_ setFrame:ovrframe];
5798 CGRect barframe = [navbar_ frame];
5799 barframe.origin.y -= ovrframe.size.height;
5800 [navbar_ setFrame:barframe];
5802 CGRect trnframe = [transition_ frame];
5803 trnframe.origin.y -= ovrframe.size.height;
5804 trnframe.size.height += ovrframe.size.height;
5805 [transition_ setFrame:trnframe];
5807 [UIView commitAnimations];
5809 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5812 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5813 if ((self = [super initWithFrame:frame]) != nil) {
5814 database_ = database;
5816 CGRect ovrrect = [navbar_ bounds];
5817 ovrrect.size.height = [UINavigationBar defaultSize].height;
5818 ovrrect.origin.y = -ovrrect.size.height;
5820 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5821 [self addSubview:overlay_];
5823 ovrrect.origin.y = frame.size.height;
5824 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5825 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5826 [self addSubview:underlay_];
5828 [overlay_ setBarStyle:1];
5829 [underlay_ setBarStyle:1];
5831 int barstyle = [overlay_ _barStyle:NO];
5832 bool ugly = barstyle == 0;
5834 UIProgressIndicatorStyle style = ugly ?
5835 UIProgressIndicatorStyleMediumBrown :
5836 UIProgressIndicatorStyleMediumWhite;
5838 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5839 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5840 CGRect indrect = {{indoffset, indoffset}, indsize};
5842 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5843 [indicator_ setStyle:style];
5844 [overlay_ addSubview:indicator_];
5846 CGSize prmsize = {215, indsize.height + 4};
5849 indoffset * 2 + indsize.width,
5853 unsigned(ovrrect.size.height - prmsize.height) / 2
5856 UIFont *font = [UIFont systemFontOfSize:15];
5858 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5860 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5861 [prompt_ setBackgroundColor:[UIColor clearColor]];
5862 [prompt_ setFont:font];
5864 [overlay_ addSubview:prompt_];
5866 CGSize prgsize = {75, 100};
5869 ovrrect.size.width - prgsize.width - 10,
5870 (ovrrect.size.height - prgsize.height) / 2
5873 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5874 [progress_ setStyle:0];
5875 [overlay_ addSubview:progress_];
5877 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5878 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5880 CGRect frame = [cancel_ frame];
5881 frame.size.width = 65;
5882 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5883 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5884 [cancel_ setFrame:frame];
5886 [cancel_ setBarStyle:barstyle];
5890 - (void) _onCancel {
5892 [cancel_ removeFromSuperview];
5895 - (void) _update { _pooled
5897 status.setDelegate(self);
5899 [database_ updateWithStatus:status];
5902 performSelectorOnMainThread:@selector(_update_)
5908 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5909 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5912 - (void) setProgressTitle:(NSString *)title {
5914 performSelectorOnMainThread:@selector(_setProgressTitle:)
5920 - (void) setProgressPercent:(float)percent {
5922 performSelectorOnMainThread:@selector(_setProgressPercent:)
5923 withObject:[NSNumber numberWithFloat:percent]
5928 - (void) startProgress {
5931 - (void) addProgressOutput:(NSString *)output {
5933 performSelectorOnMainThread:@selector(_addProgressOutput:)
5939 - (bool) isCancelling:(size_t)received {
5940 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5941 if (received_ != received) {
5942 received_ = received;
5944 } else if (now - last_ > 15)
5949 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5953 - (void) _setProgressTitle:(NSString *)title {
5954 [prompt_ setText:title];
5957 - (void) _setProgressPercent:(NSNumber *)percent {
5958 [progress_ setProgress:[percent floatValue]];
5961 - (void) _addProgressOutput:(NSString *)output {
5966 @interface CydiaURLProtocol : NSURLProtocol {
5971 @implementation CydiaURLProtocol
5973 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5974 NSURL *url([request URL]);
5977 NSString *scheme([[url scheme] lowercaseString]);
5978 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5983 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5987 - (void) startLoading {
5988 id<NSURLProtocolClient> client([self client]);
5989 NSURLRequest *request([self request]);
5991 NSURL *url([request URL]);
5992 NSString *href([url absoluteString]);
5994 NSString *path([href substringFromIndex:8]);
5995 NSRange slash([path rangeOfString:@"/"]);
5998 if (slash.location == NSNotFound) {
6002 command = [path substringToIndex:slash.location];
6003 path = [path substringFromIndex:(slash.location + 1)];
6006 Database *database([Database sharedInstance]);
6008 if ([command isEqualToString:@"package-icon"]) {
6011 Package *package([database packageWithName:path]);
6015 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
6017 UIImage *icon([package icon]);
6018 NSData *data(UIImagePNGRepresentation(icon));
6020 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
6021 [client URLProtocol:self didLoadData:data];
6022 [client URLProtocolDidFinishLoading:self];
6024 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
6028 - (void) stopLoading {
6033 @interface SignatureView : BrowserView {
6034 _transient Database *database_;
6038 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6042 @implementation SignatureView
6049 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6051 [super webView:sender didClearWindowObject:window forFrame:frame];
6054 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6055 if ((self = [super initWithBook:book]) != nil) {
6056 database_ = database;
6057 package_ = [package retain];
6062 - (void) reloadData {
6063 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6068 @interface Cydia : UIApplication <
6069 ConfirmationViewDelegate,
6070 ProgressViewDelegate,
6079 UIToolbar *buttonbar_;
6083 NSMutableArray *essential_;
6084 NSMutableArray *broken_;
6086 Database *database_;
6087 ProgressView *progress_;
6091 UIKeyboard *keyboard_;
6092 UIProgressHUD *hud_;
6094 InstallView *install_;
6095 ChangesView *changes_;
6096 ManageView *manage_;
6097 SearchView *search_;
6102 @implementation Cydia
6105 if ([broken_ count] != 0) {
6106 int count = [broken_ count];
6108 UIActionSheet *sheet = [[[UIActionSheet alloc]
6109 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6110 buttons:[NSArray arrayWithObjects:
6112 @"Ignore (Temporary)",
6114 defaultButtonIndex:0
6119 [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."];
6120 [sheet popupAlertAnimated:YES];
6121 } else if (!Ignored_ && [essential_ count] != 0) {
6122 int count = [essential_ count];
6124 UIActionSheet *sheet = [[[UIActionSheet alloc]
6125 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6126 buttons:[NSArray arrayWithObjects:
6127 @"Upgrade Essential",
6128 @"Upgrade Everything",
6129 @"Ignore (Temporary)",
6131 defaultButtonIndex:0
6136 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6137 [sheet popupAlertAnimated:YES];
6141 - (void) _reloadData {
6142 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6143 [hud setText:@"Reloading Data"];
6144 [overlay_ addSubview:hud];
6147 [database_ reloadData];
6151 [essential_ removeAllObjects];
6152 [broken_ removeAllObjects];
6154 NSArray *packages = [database_ packages];
6155 for (Package *package in packages) {
6157 [broken_ addObject:package];
6158 if ([package upgradableAndEssential:NO]) {
6159 if ([package essential])
6160 [essential_ addObject:package];
6166 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6167 [buttonbar_ setBadgeValue:badge forButton:3];
6168 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6169 [buttonbar_ setBadgeAnimated:YES forButton:3];
6170 [self setApplicationBadge:badge];
6172 [buttonbar_ setBadgeValue:nil forButton:3];
6173 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6174 [buttonbar_ setBadgeAnimated:NO forButton:3];
6175 [self removeApplicationBadge];
6181 if ([packages count] == 0);
6193 [hud removeFromSuperview];*/
6196 - (void) _saveConfig {
6198 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6203 - (void) updateData {
6206 /* XXX: this is just stupid */
6208 [install_ reloadData];
6210 [changes_ reloadData];
6212 [search_ reloadData];
6222 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6223 _assert(file != NULL);
6225 NSArray *keys = [Sources_ allKeys];
6227 for (int i(0), e([keys count]); i != e; ++i) {
6228 NSString *key = [keys objectAtIndex:i];
6229 NSDictionary *source = [Sources_ objectForKey:key];
6231 fprintf(file, "%s %s %s\n",
6232 [[source objectForKey:@"Type"] UTF8String],
6233 [[source objectForKey:@"URI"] UTF8String],
6234 [[source objectForKey:@"Distribution"] UTF8String]
6243 detachNewThreadSelector:@selector(update_)
6246 title:@"Updating Sources"
6250 - (void) reloadData {
6251 @synchronized (self) {
6252 if (confirm_ == nil)
6258 pkgProblemResolver *resolver = [database_ resolver];
6260 resolver->InstallProtect();
6261 if (!resolver->Resolve(true))
6266 [database_ prepare];
6268 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6269 [confirm_ setDelegate:self];
6271 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6272 [page setDelegate:self];
6274 [confirm_ setPage:page];
6275 [underlay_ popSubview:confirm_];
6278 - (void) installPackage:(Package *)package {
6279 @synchronized (self) {
6286 - (void) removePackage:(Package *)package {
6287 @synchronized (self) {
6294 - (void) distUpgrade {
6295 @synchronized (self) {
6296 [database_ upgrade];
6302 @synchronized (self) {
6304 if (confirm_ != nil) {
6312 [overlay_ removeFromSuperview];
6316 detachNewThreadSelector:@selector(perform)
6323 - (void) bootstrap_ {
6325 [database_ upgrade];
6326 [database_ prepare];
6327 [database_ perform];
6330 - (void) bootstrap {
6332 detachNewThreadSelector:@selector(bootstrap_)
6335 title:@"Bootstrap Install"
6339 - (void) progressViewIsComplete:(ProgressView *)progress {
6340 if (confirm_ != nil) {
6341 [underlay_ addSubview:overlay_];
6342 [confirm_ popFromSuperviewAnimated:NO];
6348 - (void) setPage:(RVPage *)page {
6349 [page resetViewAnimated:NO];
6350 [page setDelegate:self];
6351 [book_ setPage:page];
6354 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6355 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6356 [browser loadURL:url];
6360 - (void) _setHomePage {
6361 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6364 - (void) buttonBarItemTapped:(id)sender {
6365 unsigned tag = [sender tag];
6367 [book_ resetViewAnimated:YES];
6369 } else if (tag_ == 2 && tag != 2)
6370 [install_ resetView];
6373 case 1: [self _setHomePage]; break;
6375 case 2: [self setPage:install_]; break;
6376 case 3: [self setPage:changes_]; break;
6377 case 4: [self setPage:manage_]; break;
6378 case 5: [self setPage:search_]; break;
6380 default: _assert(false);
6386 - (void) applicationWillSuspend {
6388 [super applicationWillSuspend];
6391 - (void) askForSettings {
6392 UIActionSheet *role = [[[UIActionSheet alloc]
6393 initWithTitle:@"Who Are You?"
6394 buttons:[NSArray arrayWithObjects:
6395 @"User (Graphical Only)",
6396 @"Hacker (+ Command Line)",
6397 @"Developer (No Filters)",
6399 defaultButtonIndex:-1
6404 [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."];
6405 [role popupAlertAnimated:YES];
6410 [self setStatusBarShowsProgress:NO];
6413 [hud_ removeFromSuperview];
6417 pid_t pid = ExecFork();
6419 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6420 perror("launchctl stop");
6427 [self askForSettings];
6431 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6433 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6434 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6435 0, 0, screenrect.size.width, screenrect.size.height - 48
6436 ) database:database_];
6438 [book_ setDelegate:self];
6440 [overlay_ addSubview:book_];
6442 NSArray *buttonitems = [NSArray arrayWithObjects:
6443 [NSDictionary dictionaryWithObjectsAndKeys:
6444 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6445 @"home-up.png", kUIButtonBarButtonInfo,
6446 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6447 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6448 self, kUIButtonBarButtonTarget,
6449 @"Home", kUIButtonBarButtonTitle,
6450 @"0", kUIButtonBarButtonType,
6453 [NSDictionary dictionaryWithObjectsAndKeys:
6454 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6455 @"install-up.png", kUIButtonBarButtonInfo,
6456 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6457 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6458 self, kUIButtonBarButtonTarget,
6459 @"Sections", kUIButtonBarButtonTitle,
6460 @"0", kUIButtonBarButtonType,
6463 [NSDictionary dictionaryWithObjectsAndKeys:
6464 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6465 @"changes-up.png", kUIButtonBarButtonInfo,
6466 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6467 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6468 self, kUIButtonBarButtonTarget,
6469 @"Changes", kUIButtonBarButtonTitle,
6470 @"0", kUIButtonBarButtonType,
6473 [NSDictionary dictionaryWithObjectsAndKeys:
6474 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6475 @"manage-up.png", kUIButtonBarButtonInfo,
6476 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6477 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6478 self, kUIButtonBarButtonTarget,
6479 @"Manage", kUIButtonBarButtonTitle,
6480 @"0", kUIButtonBarButtonType,
6483 [NSDictionary dictionaryWithObjectsAndKeys:
6484 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6485 @"search-up.png", kUIButtonBarButtonInfo,
6486 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6487 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6488 self, kUIButtonBarButtonTarget,
6489 @"Search", kUIButtonBarButtonTitle,
6490 @"0", kUIButtonBarButtonType,
6494 buttonbar_ = [[UIToolbar alloc]
6496 withFrame:CGRectMake(
6497 0, screenrect.size.height - ButtonBarHeight_,
6498 screenrect.size.width, ButtonBarHeight_
6500 withItemList:buttonitems
6503 [buttonbar_ setDelegate:self];
6504 [buttonbar_ setBarStyle:1];
6505 [buttonbar_ setButtonBarTrackingMode:2];
6507 int buttons[5] = {1, 2, 3, 4, 5};
6508 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6509 [buttonbar_ showButtonGroup:0 withDuration:0];
6511 for (int i = 0; i != 5; ++i)
6512 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6513 i * 64 + 2, 1, 60, ButtonBarHeight_
6516 [buttonbar_ showSelectionForButton:1];
6517 [overlay_ addSubview:buttonbar_];
6519 [UIKeyboard initImplementationNow];
6520 CGSize keysize = [UIKeyboard defaultSize];
6521 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6522 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6523 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6524 [overlay_ addSubview:keyboard_];
6526 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6527 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6528 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6530 manage_ = (ManageView *) [[self
6531 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6532 withClass:[ManageView class]
6536 [underlay_ addSubview:overlay_];
6543 [self _setHomePage];
6546 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6547 NSString *context = [sheet context];
6548 if ([context isEqualToString:@"fixhalf"])
6551 @synchronized (self) {
6552 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6553 Package *broken = [broken_ objectAtIndex:i];
6556 NSString *id = [broken id];
6557 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6558 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6559 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6560 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6569 [broken_ removeAllObjects];
6576 else if ([context isEqualToString:@"role"]) {
6578 case 1: Role_ = @"User"; break;
6579 case 2: Role_ = @"Hacker"; break;
6580 case 3: Role_ = @"Developer"; break;
6587 bool reset = Settings_ != nil;
6589 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6593 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6601 } else if ([context isEqualToString:@"upgrade"])
6604 @synchronized (self) {
6605 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6606 Package *essential = [essential_ objectAtIndex:i];
6607 [essential install];
6630 - (void) reorganize { _pooled
6631 system("/usr/libexec/cydia/free.sh");
6632 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6635 - (void) applicationSuspend:(__GSEvent *)event {
6636 if (hud_ == nil && ![progress_ isRunning])
6637 [super applicationSuspend:event];
6640 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6642 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6645 - (void) _setSuspended:(BOOL)value {
6647 [super _setSuspended:value];
6650 - (UIProgressHUD *) addProgressHUD {
6651 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6653 [underlay_ addSubview:hud];
6657 - (void) openMailToURL:(NSURL *)url {
6658 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6661 - (RVPage *) pageForPackage:(NSString *)name {
6662 if (Package *package = [database_ packageWithName:name]) {
6663 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6664 [view setPackage:package];
6667 UIActionSheet *sheet = [[[UIActionSheet alloc]
6668 initWithTitle:@"Cannot Locate Package"
6669 buttons:[NSArray arrayWithObjects:@"Close", nil]
6670 defaultButtonIndex:0
6675 [sheet setBodyText:[NSString stringWithFormat:
6676 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6679 [sheet popupAlertAnimated:YES];
6684 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6685 NSString *href = [url absoluteString];
6690 if ([href isEqualToString:@"cydia://add-source"])
6691 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6692 else if ([href isEqualToString:@"cydia://sources"])
6693 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6694 else if ([href isEqualToString:@"cydia://packages"])
6695 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6696 else if ([href hasPrefix:@"cydia://url/"])
6697 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6698 else if ([href hasPrefix:@"cydia://launch/"])
6699 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6700 else if ([href hasPrefix:@"cydia://package-signature/"])
6701 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6702 else if ([href hasPrefix:@"cydia://package/"])
6703 return [self pageForPackage:[href substringFromIndex:16]];
6704 else if ([href hasPrefix:@"cydia://files/"]) {
6705 NSString *name = [href substringFromIndex:14];
6707 if (Package *package = [database_ packageWithName:name]) {
6708 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6709 [files setPackage:package];
6717 - (void) applicationOpenURL:(NSURL *)url {
6718 [super applicationOpenURL:url];
6720 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6721 [self setPage:page];
6722 [buttonbar_ showSelectionForButton:tag];
6727 - (void) applicationDidFinishLaunching:(id)unused {
6728 Font12_ = [[UIFont systemFontOfSize:12] retain];
6729 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6730 Font14_ = [[UIFont systemFontOfSize:14] retain];
6731 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6732 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6734 _assert(pkgInitConfig(*_config));
6735 _assert(pkgInitSystem(*_config, _system));
6739 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6740 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6742 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6744 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6745 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6747 [window_ orderFront:self];
6748 [window_ makeKey:self];
6749 [window_ setHidden:NO];
6751 database_ = [Database sharedInstance];
6752 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6753 [database_ setDelegate:progress_];
6754 [window_ setContentView:progress_];
6756 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6757 [progress_ setContentView:underlay_];
6759 [progress_ resetView];
6762 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6763 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6764 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6765 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6766 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6767 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6768 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6770 [self setIdleTimerDisabled:YES];
6772 hud_ = [self addProgressHUD];
6773 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6775 [self setStatusBarShowsProgress:YES];
6778 detachNewThreadSelector:@selector(reorganize)
6786 /* Web Scripting {{{ */
6787 + (NSString *) webScriptNameForSelector:(SEL)selector {
6788 if (selector == @selector(supports:))
6793 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
6794 return selector != @selector(supports:);
6797 - (BOOL) supports:(NSString *)feature {
6798 return [feature isEqualToString:@"window.open"];
6802 - (void) showKeyboard:(BOOL)show {
6803 CGSize keysize = [UIKeyboard defaultSize];
6804 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6805 CGRect keyup = keydown;
6806 keyup.origin.y -= keysize.height;
6808 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6809 [animation setSignificantRectFields:2];
6812 [animation setStartFrame:keydown];
6813 [animation setEndFrame:keyup];
6814 [keyboard_ activate];
6816 [animation setStartFrame:keyup];
6817 [animation setEndFrame:keydown];
6818 [keyboard_ deactivate];
6821 [[UIAnimator sharedAnimator]
6822 addAnimations:[NSArray arrayWithObjects:animation, nil]
6823 withDuration:KeyboardTime_
6828 - (void) slideUp:(UIActionSheet *)alert {
6830 [alert presentSheetFromButtonBar:buttonbar_];
6832 [alert presentSheetInView:overlay_];
6837 void AddPreferences(NSString *plist) { _pooled
6838 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6839 _assert(settings != NULL);
6840 NSMutableArray *items = [settings objectForKey:@"items"];
6844 for (size_t i(0); i != [items count]; ++i) {
6845 NSMutableDictionary *item([items objectAtIndex:i]);
6846 NSString *label = [item objectForKey:@"label"];
6847 if (label != nil && [label isEqualToString:@"Cydia"]) {
6854 for (size_t i(0); i != [items count]; ++i) {
6855 NSDictionary *item([items objectAtIndex:i]);
6856 NSString *label = [item objectForKey:@"label"];
6857 if (label != nil && [label isEqualToString:@"General"]) {
6858 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6859 @"CydiaSettings", @"bundle",
6860 @"PSLinkCell", @"cell",
6861 [NSNumber numberWithBool:YES], @"hasIcon",
6862 [NSNumber numberWithBool:YES], @"isController",
6864 nil] atIndex:(i + 1)];
6870 _assert([settings writeToFile:plist atomically:YES] == YES);
6875 id Alloc_(id self, SEL selector) {
6876 id object = alloc_(self, selector);
6877 lprintf("[%s]A-%p\n", self->isa->name, object);
6882 id Dealloc_(id self, SEL selector) {
6883 id object = dealloc_(self, selector);
6884 lprintf("[%s]D-%p\n", self->isa->name, object);
6888 int main(int argc, char *argv[]) { _pooled
6889 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6891 App_ = [[NSBundle mainBundle] bundlePath];
6892 Home_ = NSHomeDirectory();
6893 Locale_ = CFLocaleCopyCurrent();
6896 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6897 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6898 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6899 Sounds_Keyboard_ = [keyboard boolValue];
6905 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6906 _assert(errno == ENOENT);
6907 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6908 _assert(errno == ENOENT);
6910 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6911 alloc_ = alloc->method_imp;
6912 alloc->method_imp = (IMP) &Alloc_;*/
6914 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6915 dealloc_ = dealloc->method_imp;
6916 dealloc->method_imp = (IMP) &Dealloc_;*/
6921 size = sizeof(maxproc);
6922 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6923 perror("sysctlbyname(\"kern.maxproc\", ?)");
6924 else if (maxproc < 64) {
6926 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6927 perror("sysctlbyname(\"kern.maxproc\", #)");
6930 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6931 char *machine = new char[size];
6932 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6933 perror("sysctlbyname(\"hw.machine\", ?)");
6937 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6939 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6940 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6942 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6943 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6945 Settings_ = [Metadata_ objectForKey:@"Settings"];
6947 Packages_ = [Metadata_ objectForKey:@"Packages"];
6948 Sections_ = [Metadata_ objectForKey:@"Sections"];
6949 Sources_ = [Metadata_ objectForKey:@"Sources"];
6952 if (Settings_ != nil)
6953 Role_ = [Settings_ objectForKey:@"Role"];
6955 if (Packages_ == nil) {
6956 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6957 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6960 if (Sections_ == nil) {
6961 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6962 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6965 if (Sources_ == nil) {
6966 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6967 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6971 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
6974 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6975 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6977 if (access("/User", F_OK) != 0)
6978 system("/usr/libexec/cydia/firmware.sh");
6980 _assert([[NSFileManager defaultManager]
6981 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
6982 withIntermediateDirectories:YES
6987 space_ = CGColorSpaceCreateDeviceRGB();
6989 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6990 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6991 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6992 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
6993 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6994 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6996 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6998 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7000 UIApplicationUseLegacyEvents(YES);
7001 UIKeyboardDisableAutomaticAppearance();
7003 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7005 CGColorSpaceRelease(space_);