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 - (void) _readCydia:(NSNumber *)fd;
762 - (void) _readStatus:(NSNumber *)fd;
763 - (void) _readOutput:(NSNumber *)fd;
767 - (Package *) packageWithName:(NSString *)name;
770 - (pkgCacheFile &) cache;
771 - (pkgDepCache::Policy *) policy;
772 - (pkgRecords *) records;
773 - (pkgProblemResolver *) resolver;
774 - (pkgAcquire &) fetcher;
775 - (NSArray *) packages;
776 - (NSArray *) sources;
785 - (void) updateWithStatus:(Status &)status;
787 - (void) setDelegate:(id)delegate;
788 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
792 /* Source Class {{{ */
793 @interface Source : NSObject {
794 NSString *description_;
799 NSString *distribution_;
803 NSString *defaultIcon_;
805 NSDictionary *record_;
809 - (Source *) initWithMetaIndex:(metaIndex *)index;
811 - (NSComparisonResult) compareByNameAndType:(Source *)source;
813 - (NSDictionary *) record;
817 - (NSString *) distribution;
823 - (NSString *) description;
824 - (NSString *) label;
825 - (NSString *) origin;
826 - (NSString *) version;
828 - (NSString *) defaultIcon;
832 @implementation Source
836 [distribution_ release];
839 if (description_ != nil)
840 [description_ release];
847 if (defaultIcon_ != nil)
848 [defaultIcon_ release];
855 + (NSArray *) _attributeKeys {
856 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
859 - (NSArray *) attributeKeys {
860 return [[self class] _attributeKeys];
863 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
864 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
867 - (Source *) initWithMetaIndex:(metaIndex *)index {
868 if ((self = [super init]) != nil) {
869 trusted_ = index->IsTrusted();
871 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
872 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
873 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
875 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
876 if (dindex != NULL) {
877 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
879 while (std::getline(release, line)) {
880 std::string::size_type colon(line.find(':'));
881 if (colon == std::string::npos)
884 std::string name(line.substr(0, colon));
885 std::string value(line.substr(colon + 1));
886 while (!value.empty() && value[0] == ' ')
887 value = value.substr(1);
889 if (name == "Default-Icon")
890 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
891 else if (name == "Description")
892 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
893 else if (name == "Label")
894 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
895 else if (name == "Origin")
896 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
897 else if (name == "Version")
898 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
902 record_ = [Sources_ objectForKey:[self key]];
904 record_ = [record_ retain];
908 - (NSComparisonResult) compareByNameAndType:(Source *)source {
909 NSDictionary *lhr = [self record];
910 NSDictionary *rhr = [source record];
913 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
915 NSString *lhs = [self name];
916 NSString *rhs = [source name];
918 if ([lhs length] != 0 && [rhs length] != 0) {
919 unichar lhc = [lhs characterAtIndex:0];
920 unichar rhc = [rhs characterAtIndex:0];
922 if (isalpha(lhc) && !isalpha(rhc))
923 return NSOrderedAscending;
924 else if (!isalpha(lhc) && isalpha(rhc))
925 return NSOrderedDescending;
928 return [lhs compare:rhs options:CompareOptions_];
931 - (NSDictionary *) record {
943 - (NSString *) distribution {
944 return distribution_;
947 - (NSString *) type {
952 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
955 - (NSString *) host {
956 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
959 - (NSString *) name {
960 return origin_ == nil ? [self host] : origin_;
963 - (NSString *) description {
967 - (NSString *) label {
968 return label_ == nil ? [self host] : label_;
971 - (NSString *) origin {
975 - (NSString *) version {
979 - (NSString *) defaultIcon {
985 /* Relationship Class {{{ */
986 @interface Relationship : NSObject {
997 @implementation Relationship
1005 - (NSString *) type {
1013 - (NSString *) name {
1020 /* Package Class {{{ */
1021 NSString *Scour(const char *field, const char *begin, const char *end) {
1022 size_t i(0), l(strlen(field));
1025 const char *name = begin + i;
1026 const char *colon = name + l;
1027 const char *value = colon + 1;
1032 strncasecmp(name, field, l) == 0
1034 while (value != end && value[0] == ' ')
1036 const char *line = std::find(value, end, '\n');
1037 while (line != value && line[-1] == ' ')
1040 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1042 begin = std::find(begin, end, '\n');
1050 @interface Package : NSObject {
1051 pkgCache::PkgIterator iterator_;
1052 _transient Database *database_;
1053 pkgCache::VerIterator version_;
1054 pkgCache::VerFileIterator file_;
1060 NSString *installed_;
1066 NSString *depiction_;
1067 NSString *homepage_;
1073 NSArray *relationships_;
1076 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1077 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1079 - (pkgCache::PkgIterator) iterator;
1081 - (NSString *) section;
1082 - (Address *) maintainer;
1084 - (NSString *) description;
1085 - (NSString *) index;
1089 - (NSString *) latest;
1090 - (NSString *) installed;
1093 - (BOOL) upgradableAndEssential:(BOOL)essential;
1096 - (BOOL) unfiltered;
1100 - (BOOL) halfConfigured;
1101 - (BOOL) halfInstalled;
1103 - (NSString *) mode;
1106 - (NSString *) name;
1107 - (NSString *) tagline;
1108 - (NSString *) icon;
1109 - (NSString *) homepage;
1110 - (NSString *) depiction;
1111 - (Address *) author;
1113 - (NSArray *) files;
1114 - (NSArray *) relationships;
1115 - (NSArray *) warnings;
1116 - (NSArray *) applications;
1118 - (Source *) source;
1119 - (NSString *) role;
1121 - (BOOL) matches:(NSString *)text;
1123 - (bool) hasSupportingRole;
1124 - (BOOL) hasTag:(NSString *)tag;
1125 - (NSString *) primaryPurpose;
1127 - (NSComparisonResult) compareByName:(Package *)package;
1128 - (NSComparisonResult) compareBySection:(Package *)package;
1129 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1130 - (NSComparisonResult) compareForChanges:(Package *)package;
1135 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1136 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1137 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1138 - (NSNumber *) isVisibleInSource:(Source *)source;
1142 @implementation Package
1149 if (installed_ != nil)
1150 [installed_ release];
1158 if (depiction_ != nil)
1159 [depiction_ release];
1160 if (homepage_ != nil)
1161 [homepage_ release];
1162 if (sponsor_ != nil)
1171 if (relationships_ != nil)
1172 [relationships_ release];
1177 + (NSArray *) _attributeKeys {
1178 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1181 - (NSArray *) attributeKeys {
1182 return [[self class] _attributeKeys];
1185 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1186 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1189 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1190 if ((self = [super init]) != nil) {
1191 iterator_ = iterator;
1192 database_ = database;
1194 version_ = [database_ policy]->GetCandidateVer(iterator_);
1195 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1197 if (!version_.end())
1198 file_ = version_.FileList();
1200 pkgCache &cache([database_ cache]);
1201 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1204 pkgCache::VerIterator current = iterator_.CurrentVer();
1205 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1207 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1210 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1212 const char *begin, *end;
1213 parser->GetRec(begin, end);
1215 name_ = Scour("name", begin, end);
1217 name_ = [name_ retain];
1218 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1219 icon_ = Scour("icon", begin, end);
1221 icon_ = [icon_ retain];
1222 depiction_ = Scour("depiction", begin, end);
1223 if (depiction_ != nil)
1224 depiction_ = [depiction_ retain];
1225 homepage_ = Scour("homepage", begin, end);
1226 if (homepage_ == nil)
1227 homepage_ = Scour("website", begin, end);
1228 if ([homepage_ isEqualToString:depiction_])
1230 if (homepage_ != nil)
1231 homepage_ = [homepage_ retain];
1232 NSString *sponsor = Scour("sponsor", begin, end);
1234 sponsor_ = [[Address addressWithString:sponsor] retain];
1235 NSString *author = Scour("author", begin, end);
1237 author_ = [[Address addressWithString:author] retain];
1238 NSString *tags = Scour("tag", begin, end);
1240 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1244 for (int i(0), e([tags_ count]); i != e; ++i) {
1245 NSString *tag = [tags_ objectAtIndex:i];
1246 if ([tag hasPrefix:@"role::"]) {
1247 role_ = [[tag substringFromIndex:6] retain];
1252 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1253 if (metadata == nil || [metadata count] == 0) {
1254 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1258 [Packages_ setObject:metadata forKey:id_];
1264 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1265 return [[[Package alloc]
1266 initWithIterator:iterator
1271 - (pkgCache::PkgIterator) iterator {
1275 - (NSString *) section {
1276 const char *section = iterator_.Section();
1277 if (section == NULL)
1280 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1283 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1284 if (NSString *rename = [value objectForKey:@"Rename"]) {
1289 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1292 - (Address *) maintainer {
1295 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1296 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1300 return version_.end() ? 0 : version_->InstalledSize;
1303 - (NSString *) description {
1306 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1307 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1309 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1310 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1311 if ([lines count] < 2)
1314 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1315 for (size_t i(1); i != [lines count]; ++i) {
1316 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1317 [trimmed addObject:trim];
1320 return [trimmed componentsJoinedByString:@"\n"];
1323 - (NSString *) index {
1324 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1325 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1329 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1332 - (NSString *) latest {
1336 - (NSString *) installed {
1341 return !version_.end();
1344 - (BOOL) upgradableAndEssential:(BOOL)essential {
1345 pkgCache::VerIterator current = iterator_.CurrentVer();
1348 return essential && [self essential];
1350 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1351 return !candidate.end() && candidate != current;
1355 - (BOOL) essential {
1356 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1360 return [database_ cache][iterator_].InstBroken();
1363 - (BOOL) unfiltered {
1364 NSString *section = [self section];
1365 return section == nil || isSectionVisible(section);
1369 return [self hasSupportingRole] && [self unfiltered];
1373 unsigned char current = iterator_->CurrentState;
1374 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1377 - (BOOL) halfConfigured {
1378 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1381 - (BOOL) halfInstalled {
1382 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1386 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1387 return state.Mode != pkgDepCache::ModeKeep;
1390 - (NSString *) mode {
1391 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1393 switch (state.Mode) {
1394 case pkgDepCache::ModeDelete:
1395 if ((state.iFlags & pkgDepCache::Purge) != 0)
1399 case pkgDepCache::ModeKeep:
1400 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1404 case pkgDepCache::ModeInstall:
1405 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1406 return @"Reinstall";
1407 else switch (state.Status) {
1409 return @"Downgrade";
1415 return @"New Install";
1428 - (NSString *) name {
1429 return name_ == nil ? id_ : name_;
1432 - (NSString *) tagline {
1436 - (NSString *) icon {
1440 - (NSString *) homepage {
1444 - (NSString *) depiction {
1448 - (Address *) sponsor {
1452 - (Address *) author {
1456 - (NSArray *) files {
1457 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1458 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1461 fin.open([path UTF8String]);
1466 while (std::getline(fin, line))
1467 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1472 - (NSArray *) relationships {
1473 return relationships_;
1476 - (NSArray *) warnings {
1477 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1478 const char *name(iterator_.Name());
1480 size_t length(strlen(name));
1481 if (length < 2) invalid:
1482 [warnings addObject:@"illegal package identifier"];
1483 else for (size_t i(0); i != length; ++i)
1485 (name[i] < 'a' || name[i] > 'z') &&
1486 (name[i] < '0' || name[i] > '9') &&
1487 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1490 if (strcmp(name, "cydia") != 0) {
1494 if (NSArray *files = [self files])
1495 for (NSString *file in files)
1496 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1498 else if (!stash && [file isEqualToString:@"/var/stash"])
1502 [warnings addObject:@"files installed into Cydia.app"];
1504 [warnings addObject:@"files installed to /var/stash"];
1507 return [warnings count] == 0 ? nil : warnings;
1510 - (NSArray *) applications {
1511 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1513 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1515 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1516 if (NSArray *files = [self files])
1517 for (NSString *file in files)
1518 if (application_r(file)) {
1519 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1520 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1521 if ([id isEqualToString:me])
1524 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1526 display = application_r[1];
1528 NSString *bundle([file stringByDeletingLastPathComponent]);
1529 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1530 if (icon == nil || [icon length] == 0)
1532 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1534 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1535 [applications addObject:application];
1537 [application addObject:id];
1538 [application addObject:display];
1539 [application addObject:url];
1542 return [applications count] == 0 ? nil : applications;
1545 - (Source *) source {
1547 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1554 - (NSString *) role {
1558 - (BOOL) matches:(NSString *)text {
1564 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1565 if (range.location != NSNotFound)
1568 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1569 if (range.location != NSNotFound)
1572 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1573 if (range.location != NSNotFound)
1579 - (bool) hasSupportingRole {
1582 if ([role_ isEqualToString:@"enduser"])
1584 if ([Role_ isEqualToString:@"User"])
1586 if ([role_ isEqualToString:@"hacker"])
1588 if ([Role_ isEqualToString:@"Hacker"])
1590 if ([role_ isEqualToString:@"developer"])
1592 if ([Role_ isEqualToString:@"Developer"])
1597 - (BOOL) hasTag:(NSString *)tag {
1598 return tags_ == nil ? NO : [tags_ containsObject:tag];
1601 - (NSString *) primaryPurpose {
1602 for (NSString *tag in tags_)
1603 if ([tag hasPrefix:@"purpose::"])
1604 return [tag substringFromIndex:9];
1608 - (NSComparisonResult) compareByName:(Package *)package {
1609 NSString *lhs = [self name];
1610 NSString *rhs = [package name];
1612 if ([lhs length] != 0 && [rhs length] != 0) {
1613 unichar lhc = [lhs characterAtIndex:0];
1614 unichar rhc = [rhs characterAtIndex:0];
1616 if (isalpha(lhc) && !isalpha(rhc))
1617 return NSOrderedAscending;
1618 else if (!isalpha(lhc) && isalpha(rhc))
1619 return NSOrderedDescending;
1622 return [lhs compare:rhs options:CompareOptions_];
1625 - (NSComparisonResult) compareBySection:(Package *)package {
1626 NSString *lhs = [self section];
1627 NSString *rhs = [package section];
1629 if (lhs == NULL && rhs != NULL)
1630 return NSOrderedAscending;
1631 else if (lhs != NULL && rhs == NULL)
1632 return NSOrderedDescending;
1633 else if (lhs != NULL && rhs != NULL) {
1634 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1635 if (result != NSOrderedSame)
1639 return NSOrderedSame;
1642 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1643 NSString *lhs = [self section];
1644 NSString *rhs = [package section];
1646 if (lhs == NULL && rhs != NULL)
1647 return NSOrderedAscending;
1648 else if (lhs != NULL && rhs == NULL)
1649 return NSOrderedDescending;
1650 else if (lhs != NULL && rhs != NULL) {
1651 NSComparisonResult result = [lhs compare:rhs];
1652 if (result != NSOrderedSame)
1656 return [self compareByName:package];
1659 - (NSComparisonResult) compareForChanges:(Package *)package {
1660 BOOL lhs = [self upgradableAndEssential:YES];
1661 BOOL rhs = [package upgradableAndEssential:YES];
1664 return lhs ? NSOrderedAscending : NSOrderedDescending;
1666 switch ([[self seen] compare:[package seen]]) {
1667 case NSOrderedAscending:
1668 return NSOrderedDescending;
1671 case NSOrderedDescending:
1672 return NSOrderedAscending;
1678 return [self compareByName:package];
1682 pkgProblemResolver *resolver = [database_ resolver];
1683 resolver->Clear(iterator_);
1684 resolver->Protect(iterator_);
1685 pkgCacheFile &cache([database_ cache]);
1686 cache->MarkInstall(iterator_, false);
1687 pkgDepCache::StateCache &state((*cache)[iterator_]);
1688 if (!state.Install())
1689 cache->SetReInstall(iterator_, true);
1693 pkgProblemResolver *resolver = [database_ resolver];
1694 resolver->Clear(iterator_);
1695 resolver->Protect(iterator_);
1696 resolver->Remove(iterator_);
1697 [database_ cache]->MarkDelete(iterator_, true);
1700 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1701 return [NSNumber numberWithBool:(
1702 [self unfiltered] && [self matches:search]
1706 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1707 return [NSNumber numberWithBool:(
1708 (![number boolValue] || [self visible]) && [self installed] != nil
1712 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1713 NSString *section = [self section];
1715 return [NSNumber numberWithBool:(
1717 [self installed] == nil && (
1719 section == nil && [name length] == 0 ||
1720 [name isEqualToString:section]
1725 - (NSNumber *) isVisibleInSource:(Source *)source {
1726 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1731 /* Section Class {{{ */
1732 @interface Section : NSObject {
1738 - (NSComparisonResult) compareByName:(Section *)section;
1739 - (Section *) initWithName:(NSString *)name;
1740 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1741 - (NSString *) name;
1744 - (void) addToCount;
1748 @implementation Section
1755 - (NSComparisonResult) compareByName:(Section *)section {
1756 NSString *lhs = [self name];
1757 NSString *rhs = [section name];
1759 if ([lhs length] != 0 && [rhs length] != 0) {
1760 unichar lhc = [lhs characterAtIndex:0];
1761 unichar rhc = [rhs characterAtIndex:0];
1763 if (isalpha(lhc) && !isalpha(rhc))
1764 return NSOrderedAscending;
1765 else if (!isalpha(lhc) && isalpha(rhc))
1766 return NSOrderedDescending;
1769 return [lhs compare:rhs options:CompareOptions_];
1772 - (Section *) initWithName:(NSString *)name {
1773 return [self initWithName:name row:0];
1776 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1777 if ((self = [super init]) != nil) {
1778 name_ = [name retain];
1783 - (NSString *) name {
1795 - (void) addToCount {
1803 static NSArray *Finishes_;
1805 /* Database Implementation {{{ */
1806 @implementation Database
1813 - (void) _readCydia:(NSNumber *)fd { _pooled
1814 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1815 std::istream is(&ib);
1818 static Pcre finish_r("^finish:([^:]*)$");
1820 while (std::getline(is, line)) {
1821 const char *data(line.c_str());
1822 size_t size = line.size();
1823 lprintf("C:%s\n", data);
1825 if (finish_r(data, size)) {
1826 NSString *finish = finish_r[1];
1827 int index = [Finishes_ indexOfObject:finish];
1828 if (index != INT_MAX && index > Finish_)
1836 - (void) _readStatus:(NSNumber *)fd { _pooled
1837 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1838 std::istream is(&ib);
1841 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1842 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1844 while (std::getline(is, line)) {
1845 const char *data(line.c_str());
1846 size_t size = line.size();
1847 lprintf("S:%s\n", data);
1849 if (conffile_r(data, size)) {
1850 [delegate_ setConfigurationData:conffile_r[1]];
1851 } else if (strncmp(data, "status: ", 8) == 0) {
1852 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1853 [delegate_ setProgressTitle:string];
1854 } else if (pmstatus_r(data, size)) {
1855 std::string type([pmstatus_r[1] UTF8String]);
1856 NSString *id = pmstatus_r[2];
1858 float percent([pmstatus_r[3] floatValue]);
1859 [delegate_ setProgressPercent:(percent / 100)];
1861 NSString *string = pmstatus_r[4];
1863 if (type == "pmerror")
1864 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1865 withObject:[NSArray arrayWithObjects:string, id, nil]
1868 else if (type == "pmstatus")
1869 [delegate_ setProgressTitle:string];
1870 else if (type == "pmconffile")
1871 [delegate_ setConfigurationData:string];
1872 else _assert(false);
1873 } else _assert(false);
1879 - (void) _readOutput:(NSNumber *)fd { _pooled
1880 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1881 std::istream is(&ib);
1884 while (std::getline(is, line)) {
1885 lprintf("O:%s\n", line.c_str());
1886 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1896 - (Package *) packageWithName:(NSString *)name {
1897 if (static_cast<pkgDepCache *>(cache_) == NULL)
1899 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1900 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1903 - (Database *) init {
1904 if ((self = [super init]) != nil) {
1911 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1912 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1916 _assert(pipe(fds) != -1);
1919 _config->Set("APT::Keep-Fds::", cydiafd_);
1920 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1923 detachNewThreadSelector:@selector(_readCydia:)
1925 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1928 _assert(pipe(fds) != -1);
1932 detachNewThreadSelector:@selector(_readStatus:)
1934 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1937 _assert(pipe(fds) != -1);
1938 _assert(dup2(fds[0], 0) != -1);
1939 _assert(close(fds[0]) != -1);
1941 input_ = fdopen(fds[1], "a");
1943 _assert(pipe(fds) != -1);
1944 _assert(dup2(fds[1], 1) != -1);
1945 _assert(close(fds[1]) != -1);
1948 detachNewThreadSelector:@selector(_readOutput:)
1950 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1955 - (pkgCacheFile &) cache {
1959 - (pkgDepCache::Policy *) policy {
1963 - (pkgRecords *) records {
1967 - (pkgProblemResolver *) resolver {
1971 - (pkgAcquire &) fetcher {
1975 - (NSArray *) packages {
1979 - (NSArray *) sources {
1980 return [sources_ allValues];
1983 - (NSArray *) issues {
1984 if (cache_->BrokenCount() == 0)
1987 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
1989 for (Package *package in packages_) {
1990 if (![package broken])
1992 pkgCache::PkgIterator pkg([package iterator]);
1994 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
1995 [entry addObject:[package name]];
1996 [issues addObject:entry];
1998 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2002 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2003 pkgCache::DepIterator start;
2004 pkgCache::DepIterator end;
2005 dep.GlobOr(start, end); // ++dep
2007 if (!cache_->IsImportantDep(end))
2009 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2012 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2013 [entry addObject:failure];
2014 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2016 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2017 [failure addObject:[package name]];
2019 pkgCache::PkgIterator target(start.TargetPkg());
2020 if (target->ProvidesList != 0)
2021 [failure addObject:@"?"];
2023 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2025 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2026 else if (!cache_[target].CandidateVerIter(cache_).end())
2027 [failure addObject:@"-"];
2028 else if (target->ProvidesList == 0)
2029 [failure addObject:@"!"];
2031 [failure addObject:@"%"];
2035 if (start.TargetVer() != 0)
2036 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2047 - (void) reloadData {
2066 if (!cache_.Open(progress_, true)) {
2068 if (!_error->PopMessage(error))
2071 lprintf("cache_.Open():[%s]\n", error.c_str());
2073 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2074 [delegate_ repairWithSelector:@selector(configure)];
2075 else if (error == "The package lists or status file could not be parsed or opened.")
2076 [delegate_ repairWithSelector:@selector(update)];
2077 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2078 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2079 // else if (error == "The list of sources could not be read.")
2080 else _assert(false);
2085 now_ = [[NSDate date] retain];
2087 policy_ = new pkgDepCache::Policy();
2088 records_ = new pkgRecords(cache_);
2089 resolver_ = new pkgProblemResolver(cache_);
2090 fetcher_ = new pkgAcquire(&status_);
2093 list_ = new pkgSourceList();
2094 _assert(list_->ReadMainList());
2096 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2097 _assert(pkgApplyStatus(cache_));
2099 if (cache_->BrokenCount() != 0) {
2100 _assert(pkgFixBroken(cache_));
2101 _assert(cache_->BrokenCount() == 0);
2102 _assert(pkgMinimizeUpgrade(cache_));
2105 [sources_ removeAllObjects];
2106 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2107 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2108 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2110 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2111 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2115 [packages_ removeAllObjects];
2116 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2117 if (Package *package = [Package packageWithIterator:iterator database:self])
2118 [packages_ addObject:package];
2120 [packages_ sortUsingSelector:@selector(compareByName:)];
2123 - (void) configure {
2124 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2125 system([dpkg UTF8String]);
2133 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2134 _assert(!_error->PendingError());
2137 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2140 public pkgArchiveCleaner
2143 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2148 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2150 while (_error->PopMessage(error))
2151 lprintf("ArchiveCleaner: %s\n", error.c_str());
2156 pkgRecords records(cache_);
2158 lock_ = new FileFd();
2159 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2160 _assert(!_error->PendingError());
2163 // XXX: explain this with an error message
2164 _assert(list.ReadMainList());
2166 manager_ = (_system->CreatePM(cache_));
2167 _assert(manager_->GetArchives(fetcher_, &list, &records));
2168 _assert(!_error->PendingError());
2172 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2174 _assert(list.ReadMainList());
2175 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2176 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2179 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2184 bool failed = false;
2185 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2186 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2189 std::string uri = (*item)->DescURI();
2190 std::string error = (*item)->ErrorText;
2192 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2195 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2196 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2207 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2209 if (_error->PendingError()) {
2214 if (result == pkgPackageManager::Failed) {
2219 if (result != pkgPackageManager::Completed) {
2224 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2226 _assert(list.ReadMainList());
2227 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2228 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2231 if (![before isEqualToArray:after])
2236 _assert(pkgDistUpgrade(cache_));
2240 [self updateWithStatus:status_];
2243 - (void) updateWithStatus:(Status &)status {
2245 _assert(list.ReadMainList());
2248 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2249 _assert(!_error->PendingError());
2251 pkgAcquire fetcher(&status);
2252 _assert(list.GetIndexes(&fetcher));
2254 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2255 bool failed = false;
2256 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2257 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2258 (*item)->Finished();
2262 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2263 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2264 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2267 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2272 - (void) setDelegate:(id)delegate {
2273 delegate_ = delegate;
2274 status_.setDelegate(delegate);
2275 progress_.setDelegate(delegate);
2278 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2279 pkgIndexFile *index(NULL);
2280 list_->FindIndex(file, index);
2281 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2287 /* PopUp Windows {{{ */
2288 @interface PopUpView : UIView {
2289 _transient id delegate_;
2290 UITransitionView *transition_;
2295 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2299 @implementation PopUpView
2302 [transition_ setDelegate:nil];
2303 [transition_ release];
2309 [transition_ transition:UITransitionPushFromTop toView:nil];
2312 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2313 if (from != nil && to == nil)
2314 [self removeFromSuperview];
2317 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2318 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2319 delegate_ = delegate;
2321 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2322 [self addSubview:transition_];
2324 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2326 [view addSubview:self];
2328 [transition_ setDelegate:self];
2330 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2331 [transition_ transition:UITransitionNone toView:blank];
2332 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2339 /* Mail Composition {{{ */
2340 @interface MailToView : PopUpView {
2341 MailComposeController *controller_;
2344 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2348 @implementation MailToView
2351 [controller_ release];
2355 #include "internals.h"
2357 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2361 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2362 NSLog(@"did:%@", delivery);
2363 // [UIApp setStatusBarShowsProgress:NO];
2364 if ([controller error]){
2365 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2366 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2367 [mailAlertSheet setBodyText:[controller error]];
2368 [mailAlertSheet popupAlertAnimated:YES];
2372 - (void) showError {
2373 NSLog(@"%@", [controller_ error]);
2374 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2375 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2376 [mailAlertSheet setBodyText:[controller_ error]];
2377 [mailAlertSheet popupAlertAnimated:YES];
2380 - (void) deliverMessage { _pooled
2381 if (![controller_ deliverMessage])
2382 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2385 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2386 if ([controller_ needsDelivery])
2387 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2392 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2393 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2394 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2395 [controller_ setDelegate:self];
2396 [controller_ initializeUI];
2397 [controller_ setupForURL:url];
2399 UIView *view([controller_ view]);
2400 [overlay_ addSubview:view];
2406 /* Confirmation View {{{ */
2407 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2408 if (!iterator.end())
2409 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2410 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2412 pkgCache::PkgIterator package(dep.TargetPkg());
2415 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2422 @protocol ConfirmationViewDelegate
2427 @interface ConfirmationView : BrowserView {
2428 UIActionSheet *essential_;
2435 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2439 @implementation ConfirmationView
2446 if (essential_ != nil)
2447 [essential_ release];
2453 [book_ popFromSuperviewAnimated:YES];
2456 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2457 NSString *context = [sheet context];
2459 if ([context isEqualToString:@"remove"])
2467 [delegate_ confirm];
2472 else if ([context isEqualToString:@"unable"])
2478 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2479 [window setValue:changes_ forKey:@"changes"];
2480 [window setValue:issues_ forKey:@"issues"];
2481 [window setValue:sizes_ forKey:@"sizes"];
2482 [super webView:sender didClearWindowObject:window forFrame:frame];
2485 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2486 if ((self = [super initWithBook:book database:database]) != nil) {
2487 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2488 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2489 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2490 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2491 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2495 pkgDepCache::Policy *policy([database_ policy]);
2497 pkgCacheFile &cache([database_ cache]);
2498 NSArray *packages = [database_ packages];
2499 for (size_t i(0), e = [packages count]; i != e; ++i) {
2500 Package *package = [packages objectAtIndex:i];
2501 pkgCache::PkgIterator iterator = [package iterator];
2502 pkgDepCache::StateCache &state(cache[iterator]);
2504 NSString *name([package name]);
2506 if (state.NewInstall())
2507 [installing addObject:name];
2508 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2509 [reinstalling addObject:name];
2510 else if (state.Upgrade())
2511 [upgrading addObject:name];
2512 else if (state.Downgrade())
2513 [downgrading addObject:name];
2514 else if (state.Delete()) {
2515 if ([package essential])
2517 [removing addObject:name];
2520 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2521 substrate_ |= DepSubstrate(iterator.CurrentVer());
2526 else if (Advanced_ || true) {
2527 essential_ = [[UIActionSheet alloc]
2528 initWithTitle:@"Removing Essentials"
2529 buttons:[NSArray arrayWithObjects:
2530 @"Cancel Operation (Safe)",
2531 @"Force Removal (Unsafe)",
2533 defaultButtonIndex:0
2539 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2541 [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."];
2543 essential_ = [[UIActionSheet alloc]
2544 initWithTitle:@"Unable to Comply"
2545 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2546 defaultButtonIndex:0
2551 [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."];
2554 changes_ = [[NSArray alloc] initWithObjects:
2562 issues_ = [database_ issues];
2564 issues_ = [issues_ retain];
2566 sizes_ = [[NSArray alloc] initWithObjects:
2567 SizeString([database_ fetcher].FetchNeeded()),
2568 SizeString([database_ fetcher].PartialPresent()),
2569 SizeString([database_ cache]->UsrSize()),
2572 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2576 // XXX: replace with <title/>
2577 - (NSString *) title {
2578 return issues_ == nil ? @"Confirm Changes" : @"Cannot Comply";
2581 - (NSString *) backButtonTitle {
2585 - (NSString *) leftButtonTitle {
2589 - (NSString *) _rightButtonTitle {
2590 return issues_ == nil ? @"Confirm" : nil;
2593 - (void) _leftButtonClicked {
2597 - (void) _rightButtonClicked {
2598 if (essential_ != nil)
2599 [essential_ popupAlertAnimated:YES];
2603 [delegate_ confirm];
2610 /* Progress Data {{{ */
2611 @interface ProgressData : NSObject {
2617 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2624 @implementation ProgressData
2626 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2627 if ((self = [super init]) != nil) {
2628 selector_ = selector;
2648 /* Progress View {{{ */
2649 @interface ProgressView : UIView <
2650 ConfigurationDelegate,
2653 _transient Database *database_;
2655 UIView *background_;
2656 UITransitionView *transition_;
2658 UINavigationBar *navbar_;
2659 UIProgressBar *progress_;
2660 UITextView *output_;
2661 UITextLabel *status_;
2662 UIPushButton *close_;
2665 SHA1SumValue springlist_;
2666 SHA1SumValue sandplate_;
2668 NSTimeInterval last_;
2671 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2673 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2674 - (void) setContentView:(UIView *)view;
2677 - (void) _retachThread;
2678 - (void) _detachNewThreadData:(ProgressData *)data;
2679 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2685 @protocol ProgressViewDelegate
2686 - (void) progressViewIsComplete:(ProgressView *)sender;
2689 @implementation ProgressView
2692 [transition_ setDelegate:nil];
2693 [navbar_ setDelegate:nil];
2696 if (background_ != nil)
2697 [background_ release];
2698 [transition_ release];
2701 [progress_ release];
2708 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2709 if (bootstrap_ && from == overlay_ && to == view_)
2713 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2714 if ((self = [super initWithFrame:frame]) != nil) {
2715 database_ = database;
2716 delegate_ = delegate;
2718 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2719 [transition_ setDelegate:self];
2721 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2724 [overlay_ setBackgroundColor:[UIColor blackColor]];
2726 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2727 [background_ setBackgroundColor:[UIColor blackColor]];
2728 [self addSubview:background_];
2731 [self addSubview:transition_];
2733 CGSize navsize = [UINavigationBar defaultSize];
2734 CGRect navrect = {{0, 0}, navsize};
2736 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2737 [overlay_ addSubview:navbar_];
2739 [navbar_ setBarStyle:1];
2740 [navbar_ setDelegate:self];
2742 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2743 [navbar_ pushNavigationItem:navitem];
2745 CGRect bounds = [overlay_ bounds];
2746 CGSize prgsize = [UIProgressBar defaultSize];
2749 (bounds.size.width - prgsize.width) / 2,
2750 bounds.size.height - prgsize.height - 20
2753 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2754 [progress_ setStyle:0];
2756 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2758 bounds.size.height - prgsize.height - 50,
2759 bounds.size.width - 20,
2763 [status_ setColor:[UIColor whiteColor]];
2764 [status_ setBackgroundColor:[UIColor clearColor]];
2766 [status_ setCentersHorizontally:YES];
2767 //[status_ setFont:font];
2769 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2771 navrect.size.height + 20,
2772 bounds.size.width - 20,
2773 bounds.size.height - navsize.height - 62 - navrect.size.height
2776 //[output_ setTextFont:@"Courier New"];
2777 [output_ setTextSize:12];
2779 [output_ setTextColor:[UIColor whiteColor]];
2780 [output_ setBackgroundColor:[UIColor clearColor]];
2782 [output_ setMarginTop:0];
2783 [output_ setAllowsRubberBanding:YES];
2784 [output_ setEditable:NO];
2786 [overlay_ addSubview:output_];
2788 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2790 bounds.size.height - prgsize.height - 50,
2791 bounds.size.width - 20,
2795 [close_ setAutosizesToFit:NO];
2796 [close_ setDrawsShadow:YES];
2797 [close_ setStretchBackground:YES];
2798 [close_ setEnabled:YES];
2800 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2801 [close_ setTitleFont:bold];
2803 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2804 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2805 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2809 - (void) setContentView:(UIView *)view {
2810 view_ = [view retain];
2813 - (void) resetView {
2814 [transition_ transition:6 toView:view_];
2817 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2818 NSString *context = [sheet context];
2819 if ([context isEqualToString:@"conffile"]) {
2820 FILE *input = [database_ input];
2824 fprintf(input, "N\n");
2828 fprintf(input, "Y\n");
2839 - (void) closeButtonPushed {
2848 [delegate_ suspendWithAnimation:YES];
2852 system("launchctl stop com.apple.SpringBoard");
2856 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
2865 - (void) _retachThread {
2866 UINavigationItem *item = [navbar_ topItem];
2867 [item setTitle:@"Complete"];
2869 [overlay_ addSubview:close_];
2870 [progress_ removeFromSuperview];
2871 [status_ removeFromSuperview];
2873 [delegate_ progressViewIsComplete:self];
2876 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
2877 MMap mmap(file, MMap::ReadOnly);
2879 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2880 if (!(sandplate_ == sha1.Result()))
2885 FileFd file(SpringBoard_, FileFd::ReadOnly);
2886 MMap mmap(file, MMap::ReadOnly);
2888 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2889 if (!(springlist_ == sha1.Result()))
2894 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2895 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2896 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2897 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2898 case 4: [close_ setTitle:@"Reboot Device"]; break;
2901 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
2903 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
2904 [cache autorelease];
2906 NSFileManager *manager = [NSFileManager defaultManager];
2907 NSError *error = nil;
2909 id system = [cache objectForKey:@"System"];
2914 if (stat(Cache_, &info) == -1)
2917 [system removeAllObjects];
2919 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
2920 for (NSString *app in apps)
2921 if ([app hasSuffix:@".app"]) {
2922 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
2923 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
2924 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
2926 [info setObject:path forKey:@"Path"];
2927 [info setObject:@"System" forKey:@"ApplicationType"];
2928 [system addInfoDictionary:info];
2933 [cache writeToFile:@Cache_ atomically:YES];
2935 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
2937 if (chmod(Cache_, info.st_mode) == -1)
2941 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
2944 notify_post("com.apple.mobile.application_installed");
2946 [delegate_ setStatusBarShowsProgress:NO];
2949 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
2950 [[data target] performSelector:[data selector] withObject:[data object]];
2953 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2956 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2957 UINavigationItem *item = [navbar_ topItem];
2958 [item setTitle:title];
2960 [status_ setText:nil];
2961 [output_ setText:@""];
2962 [progress_ setProgress:0];
2965 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
2967 [close_ removeFromSuperview];
2968 [overlay_ addSubview:progress_];
2969 [overlay_ addSubview:status_];
2971 [delegate_ setStatusBarShowsProgress:YES];
2975 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
2976 MMap mmap(file, MMap::ReadOnly);
2978 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2979 sandplate_ = sha1.Result();
2983 FileFd file(SpringBoard_, FileFd::ReadOnly);
2984 MMap mmap(file, MMap::ReadOnly);
2986 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2987 springlist_ = sha1.Result();
2990 [transition_ transition:6 toView:overlay_];
2993 detachNewThreadSelector:@selector(_detachNewThreadData:)
2995 withObject:[[ProgressData alloc]
2996 initWithSelector:selector
3003 - (void) repairWithSelector:(SEL)selector {
3005 detachNewThreadSelector:selector
3012 - (void) setConfigurationData:(NSString *)data {
3014 performSelectorOnMainThread:@selector(_setConfigurationData:)
3020 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3021 Package *package = id == nil ? nil : [database_ packageWithName:id];
3023 UIActionSheet *sheet = [[[UIActionSheet alloc]
3024 initWithTitle:(package == nil ? id : [package name])
3025 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3026 defaultButtonIndex:0
3031 [sheet setBodyText:error];
3032 [sheet popupAlertAnimated:YES];
3035 - (void) setProgressTitle:(NSString *)title {
3037 performSelectorOnMainThread:@selector(_setProgressTitle:)
3043 - (void) setProgressPercent:(float)percent {
3045 performSelectorOnMainThread:@selector(_setProgressPercent:)
3046 withObject:[NSNumber numberWithFloat:percent]
3051 - (void) startProgress {
3052 last_ = [NSDate timeIntervalSinceReferenceDate];
3055 - (void) addProgressOutput:(NSString *)output {
3057 performSelectorOnMainThread:@selector(_addProgressOutput:)
3063 - (bool) isCancelling:(size_t)received {
3065 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3066 if (received_ != received) {
3067 received_ = received;
3069 } else if (now - last_ > 30)
3076 - (void) _setConfigurationData:(NSString *)data {
3077 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3079 _assert(conffile_r(data));
3081 NSString *ofile = conffile_r[1];
3082 //NSString *nfile = conffile_r[2];
3084 UIActionSheet *sheet = [[[UIActionSheet alloc]
3085 initWithTitle:@"Configuration Upgrade"
3086 buttons:[NSArray arrayWithObjects:
3087 @"Keep My Old Copy",
3088 @"Accept The New Copy",
3089 // XXX: @"See What Changed",
3091 defaultButtonIndex:0
3096 [sheet setBodyText:[NSString stringWithFormat:
3097 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3100 [sheet popupAlertAnimated:YES];
3103 - (void) _setProgressTitle:(NSString *)title {
3104 [status_ setText:title];
3107 - (void) _setProgressPercent:(NSNumber *)percent {
3108 [progress_ setProgress:[percent floatValue]];
3111 - (void) _addProgressOutput:(NSString *)output {
3112 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3113 CGSize size = [output_ contentSize];
3114 CGRect rect = {{0, size.height}, {size.width, 0}};
3115 [output_ scrollRectToVisible:rect animated:YES];
3118 - (BOOL) isRunning {
3125 /* Package Cell {{{ */
3126 @interface PackageCell : UISimpleTableCell {
3129 NSString *description_;
3133 UITextLabel *status_;
3137 - (PackageCell *) init;
3138 - (void) setPackage:(Package *)package;
3140 + (int) heightForPackage:(Package *)package;
3144 @implementation PackageCell
3146 - (void) clearPackage {
3157 if (description_ != nil) {
3158 [description_ release];
3162 if (source_ != nil) {
3167 if (badge_ != nil) {
3174 [self clearPackage];
3181 - (PackageCell *) init {
3182 if ((self = [super init]) != nil) {
3184 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3185 [status_ setBackgroundColor:[UIColor clearColor]];
3186 [status_ setFont:small];
3191 - (void) setPackage:(Package *)package {
3192 [self clearPackage];
3194 Source *source = [package source];
3195 NSString *section = [package section];
3197 section = Simplify(section);
3200 if (NSString *icon = [package icon])
3201 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
3202 if (icon_ == nil) if (section != nil)
3203 icon_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
3204 if (icon_ == nil) if (NSString *icon = [source defaultIcon])
3205 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
3207 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3209 icon_ = [icon_ retain];
3211 name_ = [[package name] retain];
3212 description_ = [[package tagline] retain];
3214 NSString *label = nil;
3215 bool trusted = false;
3217 if (source != nil) {
3218 label = [source label];
3219 trusted = [source trusted];
3220 } else if ([[package id] isEqualToString:@"firmware"])
3223 label = @"Unknown/Local";
3225 NSString *from = [NSString stringWithFormat:@"from %@", label];
3227 if (section != nil && ![section isEqualToString:label])
3228 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3230 source_ = [from retain];
3232 if (NSString *purpose = [package primaryPurpose])
3233 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3234 badge_ = [badge_ retain];
3237 if (NSString *mode = [package mode]) {
3238 [badge_ setImage:[UIImage applicationImageNamed:
3239 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3242 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3243 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3244 } else if ([package half]) {
3245 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3246 [status_ setText:@"Package Damaged"];
3247 [status_ setColor:[UIColor redColor]];
3249 [badge_ setImage:nil];
3250 [status_ setText:nil];
3255 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3258 rect.size = [icon_ size];
3260 rect.size.width /= 2;
3261 rect.size.height /= 2;
3263 rect.origin.x = 25 - rect.size.width / 2;
3264 rect.origin.y = 25 - rect.size.height / 2;
3266 [icon_ drawInRect:rect];
3269 if (badge_ != nil) {
3270 CGSize size = [badge_ size];
3272 [badge_ drawAtPoint:CGPointMake(
3273 36 - size.width / 2,
3274 36 - size.height / 2
3283 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3284 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3288 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3290 [super drawContentInRect:rect selected:selected];
3293 + (int) heightForPackage:(Package *)package {
3294 NSString *tagline([package tagline]);
3295 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3297 if ([package hasMode] || [package half])
3306 /* Section Cell {{{ */
3307 @interface SectionCell : UISimpleTableCell {
3312 _UISwitchSlider *switch_;
3317 - (void) setSection:(Section *)section editing:(BOOL)editing;
3321 @implementation SectionCell
3323 - (void) clearSection {
3324 if (section_ != nil) {
3334 if (count_ != nil) {
3341 [self clearSection];
3348 if ((self = [super init]) != nil) {
3349 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3351 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3352 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3356 - (void) onSwitch:(id)sender {
3357 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3358 if (metadata == nil) {
3359 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3360 [Sections_ setObject:metadata forKey:section_];
3364 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3367 - (void) setSection:(Section *)section editing:(BOOL)editing {
3368 if (editing != editing_) {
3370 [switch_ removeFromSuperview];
3372 [self addSubview:switch_];
3376 [self clearSection];
3378 if (section == nil) {
3379 name_ = [@"All Packages" retain];
3382 section_ = [section name];
3383 if (section_ != nil)
3384 section_ = [section_ retain];
3385 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3386 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3389 [switch_ setValue:isSectionVisible(section_) animated:NO];
3393 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3394 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3401 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3403 CGSize size = [count_ sizeWithFont:Font14_];
3407 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3409 [super drawContentInRect:rect selected:selected];
3415 /* File Table {{{ */
3416 @interface FileTable : RVPage {
3417 _transient Database *database_;
3420 NSMutableArray *files_;
3424 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3425 - (void) setPackage:(Package *)package;
3429 @implementation FileTable
3432 if (package_ != nil)
3441 - (int) numberOfRowsInTable:(UITable *)table {
3442 return files_ == nil ? 0 : [files_ count];
3445 - (float) table:(UITable *)table heightForRow:(int)row {
3449 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3450 if (reusing == nil) {
3451 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3452 UIFont *font = [UIFont systemFontOfSize:16];
3453 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3455 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3459 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3463 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3464 if ((self = [super initWithBook:book]) != nil) {
3465 database_ = database;
3467 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3469 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3470 [self addSubview:list_];
3472 UITableColumn *column = [[[UITableColumn alloc]
3473 initWithTitle:@"Name"
3475 width:[self frame].size.width
3478 [list_ setDataSource:self];
3479 [list_ setSeparatorStyle:1];
3480 [list_ addTableColumn:column];
3481 [list_ setDelegate:self];
3482 [list_ setReusesTableCells:YES];
3486 - (void) setPackage:(Package *)package {
3487 if (package_ != nil) {
3488 [package_ autorelease];
3497 [files_ removeAllObjects];
3499 if (package != nil) {
3500 package_ = [package retain];
3501 name_ = [[package id] retain];
3503 if (NSArray *files = [package files])
3504 [files_ addObjectsFromArray:files];
3506 if ([files_ count] != 0) {
3507 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3508 [files_ removeObjectAtIndex:0];
3509 [files_ sortUsingSelector:@selector(compareByPath:)];
3511 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3512 [stack addObject:@"/"];
3514 for (int i(0), e([files_ count]); i != e; ++i) {
3515 NSString *file = [files_ objectAtIndex:i];
3516 while (![file hasPrefix:[stack lastObject]])
3517 [stack removeLastObject];
3518 NSString *directory = [stack lastObject];
3519 [stack addObject:[file stringByAppendingString:@"/"]];
3520 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3521 ([stack count] - 2) * 3, "",
3522 [file substringFromIndex:[directory length]]
3531 - (void) resetViewAnimated:(BOOL)animated {
3532 [list_ resetViewAnimated:animated];
3535 - (void) reloadData {
3536 [self setPackage:[database_ packageWithName:name_]];
3537 [self reloadButtons];
3540 - (NSString *) title {
3541 return @"Installed Files";
3544 - (NSString *) backButtonTitle {
3550 /* Package View {{{ */
3551 @interface PackageView : BrowserView {
3554 NSMutableArray *buttons_;
3557 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3558 - (void) setPackage:(Package *)package;
3562 @implementation PackageView
3565 if (package_ != nil)
3573 - (void) _clickButtonWithName:(NSString *)name {
3574 if ([name isEqualToString:@"Install"])
3575 [delegate_ installPackage:package_];
3576 else if ([name isEqualToString:@"Reinstall"])
3577 [delegate_ installPackage:package_];
3578 else if ([name isEqualToString:@"Remove"])
3579 [delegate_ removePackage:package_];
3580 else if ([name isEqualToString:@"Upgrade"])
3581 [delegate_ installPackage:package_];
3582 else _assert(false);
3585 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3586 int count = [buttons_ count];
3587 _assert(count != 0);
3588 _assert(button <= count + 1);
3590 if (count != button - 1)
3591 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3596 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3597 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3598 return [super webView:sender didFinishLoadForFrame:frame];
3601 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3602 [window setValue:package_ forKey:@"package"];
3603 [super webView:sender didClearWindowObject:window forFrame:frame];
3607 - (void) _rightButtonClicked {
3608 /*[super _rightButtonClicked];
3611 int count = [buttons_ count];
3612 _assert(count != 0);
3615 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3617 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3618 [buttons addObjectsFromArray:buttons_];
3619 [buttons addObject:@"Cancel"];
3621 [delegate_ slideUp:[[[UIActionSheet alloc]
3624 defaultButtonIndex:2
3632 - (NSString *) _rightButtonTitle {
3633 int count = [buttons_ count];
3634 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3637 - (NSString *) backButtonTitle {
3641 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3642 if ((self = [super initWithBook:book database:database]) != nil) {
3643 database_ = database;
3644 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3648 - (void) setPackage:(Package *)package {
3649 if (package_ != nil) {
3650 [package_ autorelease];
3659 [buttons_ removeAllObjects];
3661 if (package != nil) {
3662 package_ = [package retain];
3663 name_ = [[package id] retain];
3665 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3667 if ([package_ source] == nil);
3668 else if ([package_ upgradableAndEssential:NO])
3669 [buttons_ addObject:@"Upgrade"];
3670 else if ([package_ installed] == nil)
3671 [buttons_ addObject:@"Install"];
3673 [buttons_ addObject:@"Reinstall"];
3674 if ([package_ installed] != nil)
3675 [buttons_ addObject:@"Remove"];
3679 - (void) reloadData {
3680 [self setPackage:[database_ packageWithName:name_]];
3681 [self reloadButtons];
3686 /* Package Table {{{ */
3687 @interface PackageTable : RVPage {
3688 _transient Database *database_;
3692 NSMutableArray *packages_;
3693 NSMutableArray *sections_;
3694 UISectionList *list_;
3697 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3699 - (void) setDelegate:(id)delegate;
3700 - (void) setObject:(id)object;
3702 - (void) reloadData;
3703 - (void) resetCursor;
3705 - (UISectionList *) list;
3707 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3711 @implementation PackageTable
3714 [list_ setDataSource:nil];
3719 [packages_ release];
3720 [sections_ release];
3725 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3726 return [sections_ count];
3729 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3730 return [[sections_ objectAtIndex:section] name];
3733 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3734 return [[sections_ objectAtIndex:section] row];
3737 - (int) numberOfRowsInTable:(UITable *)table {
3738 return [packages_ count];
3741 - (float) table:(UITable *)table heightForRow:(int)row {
3742 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3745 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3747 reusing = [[[PackageCell alloc] init] autorelease];
3748 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3752 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3756 - (void) tableRowSelected:(NSNotification *)notification {
3757 int row = [[notification object] selectedRow];
3761 Package *package = [packages_ objectAtIndex:row];
3762 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3763 [view setDelegate:delegate_];
3764 [view setPackage:package];
3765 [book_ pushPage:view];
3768 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3769 if ((self = [super initWithBook:book]) != nil) {
3770 database_ = database;
3771 title_ = [title retain];
3773 object_ = object == nil ? nil : [object retain];
3775 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3776 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3778 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3779 [list_ setDataSource:self];
3781 UITableColumn *column = [[[UITableColumn alloc]
3782 initWithTitle:@"Name"
3784 width:[self frame].size.width
3787 UITable *table = [list_ table];
3788 [table setSeparatorStyle:1];
3789 [table addTableColumn:column];
3790 [table setDelegate:self];
3791 [table setReusesTableCells:YES];
3793 [self addSubview:list_];
3796 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3797 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3801 - (void) setDelegate:(id)delegate {
3802 delegate_ = delegate;
3805 - (void) setObject:(id)object {
3811 object_ = [object retain];
3814 - (void) reloadData {
3815 NSArray *packages = [database_ packages];
3817 [packages_ removeAllObjects];
3818 [sections_ removeAllObjects];
3820 for (size_t i(0); i != [packages count]; ++i) {
3821 Package *package([packages objectAtIndex:i]);
3822 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3823 [packages_ addObject:package];
3826 Section *section = nil;
3828 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3829 Package *package = [packages_ objectAtIndex:offset];
3830 NSString *name = [package index];
3832 if (section == nil || ![[section name] isEqualToString:name]) {
3833 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3834 [sections_ addObject:section];
3837 [section addToCount];
3843 - (NSString *) title {
3847 - (void) resetViewAnimated:(BOOL)animated {
3848 [list_ resetViewAnimated:animated];
3851 - (void) resetCursor {
3852 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3855 - (UISectionList *) list {
3859 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3860 [list_ setShouldHideHeaderInShortLists:hide];
3866 /* Add Source View {{{ */
3867 @interface AddSourceView : RVPage {
3868 _transient Database *database_;
3871 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3875 @implementation AddSourceView
3877 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3878 if ((self = [super initWithBook:book]) != nil) {
3879 database_ = database;
3885 /* Source Cell {{{ */
3886 @interface SourceCell : UITableCell {
3889 NSString *description_;
3895 - (SourceCell *) initWithSource:(Source *)source;
3899 @implementation SourceCell
3904 [description_ release];
3909 - (SourceCell *) initWithSource:(Source *)source {
3910 if ((self = [super init]) != nil) {
3912 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3914 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3915 icon_ = [icon_ retain];
3917 origin_ = [[source name] retain];
3918 label_ = [[source uri] retain];
3919 description_ = [[source description] retain];
3923 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3925 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3932 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3936 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3940 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3942 [super drawContentInRect:rect selected:selected];
3947 /* Source Table {{{ */
3948 @interface SourceTable : RVPage {
3949 _transient Database *database_;
3950 UISectionList *list_;
3951 NSMutableArray *sources_;
3952 UIActionSheet *alert_;
3956 UIProgressHUD *hud_;
3959 //NSURLConnection *installer_;
3960 NSURLConnection *trivial_bz2_;
3961 NSURLConnection *trivial_gz_;
3962 //NSURLConnection *automatic_;
3967 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3971 @implementation SourceTable
3973 - (void) _deallocConnection:(NSURLConnection *)connection {
3974 if (connection != nil) {
3975 [connection cancel];
3976 //[connection setDelegate:nil];
3977 [connection release];
3982 [[list_ table] setDelegate:nil];
3983 [list_ setDataSource:nil];
3992 //[self _deallocConnection:installer_];
3993 [self _deallocConnection:trivial_gz_];
3994 [self _deallocConnection:trivial_bz2_];
3995 //[self _deallocConnection:automatic_];
4002 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4003 return offset_ == 0 ? 1 : 2;
4006 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4007 switch (section + (offset_ == 0 ? 1 : 0)) {
4008 case 0: return @"Entered by User";
4009 case 1: return @"Installed by Packages";
4017 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4018 switch (section + (offset_ == 0 ? 1 : 0)) {
4020 case 1: return offset_;
4028 - (int) numberOfRowsInTable:(UITable *)table {
4029 return [sources_ count];
4032 - (float) table:(UITable *)table heightForRow:(int)row {
4033 Source *source = [sources_ objectAtIndex:row];
4034 return [source description] == nil ? 56 : 73;
4037 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4038 Source *source = [sources_ objectAtIndex:row];
4039 // XXX: weird warning, stupid selectors ;P
4040 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4043 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4047 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4051 - (void) tableRowSelected:(NSNotification*)notification {
4052 UITable *table([list_ table]);
4053 int row([table selectedRow]);
4057 Source *source = [sources_ objectAtIndex:row];
4059 PackageTable *packages = [[[PackageTable alloc]
4062 title:[source label]
4063 filter:@selector(isVisibleInSource:)
4067 [packages setDelegate:delegate_];
4069 [book_ pushPage:packages];
4072 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4073 Source *source = [sources_ objectAtIndex:row];
4074 return [source record] != nil;
4077 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4078 [[list_ table] setDeleteConfirmationRow:row];
4081 - (void) table:(UITable *)table deleteRow:(int)row {
4082 Source *source = [sources_ objectAtIndex:row];
4083 [Sources_ removeObjectForKey:[source key]];
4084 [delegate_ syncData];
4087 - (void) _endConnection:(NSURLConnection *)connection {
4088 NSURLConnection **field = NULL;
4089 if (connection == trivial_bz2_)
4090 field = &trivial_bz2_;
4091 else if (connection == trivial_gz_)
4092 field = &trivial_gz_;
4093 _assert(field != NULL);
4094 [connection release];
4098 trivial_bz2_ == nil &&
4101 [delegate_ setStatusBarShowsProgress:NO];
4104 [hud_ removeFromSuperview];
4109 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4112 @"./", @"Distribution",
4113 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4115 [delegate_ syncData];
4116 } else if (error_ != nil) {
4117 UIActionSheet *sheet = [[[UIActionSheet alloc]
4118 initWithTitle:@"Verification Error"
4119 buttons:[NSArray arrayWithObjects:@"OK", nil]
4120 defaultButtonIndex:0
4125 [sheet setBodyText:[error_ localizedDescription]];
4126 [sheet popupAlertAnimated:YES];
4128 UIActionSheet *sheet = [[[UIActionSheet alloc]
4129 initWithTitle:@"Did not Find Repository"
4130 buttons:[NSArray arrayWithObjects:@"OK", nil]
4131 defaultButtonIndex:0
4136 [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."];
4137 [sheet popupAlertAnimated:YES];
4143 if (error_ != nil) {
4150 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4151 switch ([response statusCode]) {
4157 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4158 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4160 error_ = [error retain];
4161 [self _endConnection:connection];
4164 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4165 [self _endConnection:connection];
4168 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4169 NSMutableURLRequest *request = [NSMutableURLRequest
4170 requestWithURL:[NSURL URLWithString:href]
4171 cachePolicy:NSURLRequestUseProtocolCachePolicy
4172 timeoutInterval:20.0
4175 [request setHTTPMethod:method];
4177 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4180 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4181 NSString *context = [sheet context];
4182 if ([context isEqualToString:@"source"])
4185 NSString *href = [[sheet textField] text];
4187 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4189 if (![href hasSuffix:@"/"])
4190 href_ = [href stringByAppendingString:@"/"];
4193 href_ = [href_ retain];
4195 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4196 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4197 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4201 hud_ = [delegate_ addProgressHUD];
4202 [hud_ setText:@"Verifying URL"];
4215 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4216 if ((self = [super initWithBook:book]) != nil) {
4217 database_ = database;
4218 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4220 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4221 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4222 [list_ setShouldHideHeaderInShortLists:NO];
4224 [self addSubview:list_];
4225 [list_ setDataSource:self];
4227 UITableColumn *column = [[UITableColumn alloc]
4228 initWithTitle:@"Name"
4230 width:[self frame].size.width
4233 UITable *table = [list_ table];
4234 [table setSeparatorStyle:1];
4235 [table addTableColumn:column];
4236 [table setDelegate:self];
4240 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4241 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4245 - (void) reloadData {
4247 _assert(list.ReadMainList());
4249 [sources_ removeAllObjects];
4250 [sources_ addObjectsFromArray:[database_ sources]];
4251 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4253 int count = [sources_ count];
4254 for (offset_ = 0; offset_ != count; ++offset_) {
4255 Source *source = [sources_ objectAtIndex:offset_];
4256 if ([source record] == nil)
4263 - (void) resetViewAnimated:(BOOL)animated {
4264 [list_ resetViewAnimated:animated];
4267 - (void) _leftButtonClicked {
4268 /*[book_ pushPage:[[[AddSourceView alloc]
4273 UIActionSheet *sheet = [[[UIActionSheet alloc]
4274 initWithTitle:@"Enter Cydia/APT URL"
4275 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4276 defaultButtonIndex:0
4281 [sheet addTextFieldWithValue:@"http://" label:@""];
4283 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4284 [traits setAutocapitalizationType:0];
4285 [traits setKeyboardType:3];
4286 [traits setAutocorrectionType:1];
4288 [sheet popupAlertAnimated:YES];
4291 - (void) _rightButtonClicked {
4292 UITable *table = [list_ table];
4293 BOOL editing = [table isRowDeletionEnabled];
4294 [table enableRowDeletion:!editing animated:YES];
4295 [book_ reloadButtonsForPage:self];
4298 - (NSString *) title {
4302 - (NSString *) leftButtonTitle {
4303 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4306 - (NSString *) rightButtonTitle {
4307 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4310 - (UINavigationButtonStyle) rightButtonStyle {
4311 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4317 /* Installed View {{{ */
4318 @interface InstalledView : RVPage {
4319 _transient Database *database_;
4320 PackageTable *packages_;
4324 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4328 @implementation InstalledView
4331 [packages_ release];
4335 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4336 if ((self = [super initWithBook:book]) != nil) {
4337 database_ = database;
4339 packages_ = [[PackageTable alloc]
4343 filter:@selector(isInstalledAndVisible:)
4344 with:[NSNumber numberWithBool:YES]
4347 [self addSubview:packages_];
4349 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4350 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4354 - (void) resetViewAnimated:(BOOL)animated {
4355 [packages_ resetViewAnimated:animated];
4358 - (void) reloadData {
4359 [packages_ reloadData];
4362 - (void) _rightButtonClicked {
4363 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4364 [packages_ reloadData];
4366 [book_ reloadButtonsForPage:self];
4369 - (NSString *) title {
4370 return @"Installed";
4373 - (NSString *) backButtonTitle {
4377 - (NSString *) rightButtonTitle {
4378 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4381 - (UINavigationButtonStyle) rightButtonStyle {
4382 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4385 - (void) setDelegate:(id)delegate {
4386 [super setDelegate:delegate];
4387 [packages_ setDelegate:delegate];
4394 @interface HomeView : BrowserView {
4399 @implementation HomeView
4401 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4405 - (void) _leftButtonClicked {
4406 UIActionSheet *sheet = [[[UIActionSheet alloc]
4407 initWithTitle:@"About Cydia Installer"
4408 buttons:[NSArray arrayWithObjects:@"Close", nil]
4409 defaultButtonIndex:0
4415 @"Copyright (C) 2008\n"
4416 "Jay Freeman (saurik)\n"
4417 "saurik@saurik.com\n"
4418 "http://www.saurik.com/\n"
4421 "http://www.theokorigroup.com/\n"
4423 "College of Creative Studies,\n"
4424 "University of California,\n"
4426 "http://www.ccs.ucsb.edu/"
4429 [sheet popupAlertAnimated:YES];
4432 - (NSString *) leftButtonTitle {
4438 /* Manage View {{{ */
4439 @interface ManageView : BrowserView {
4444 @implementation ManageView
4446 - (NSString *) title {
4450 - (void) _leftButtonClicked {
4451 [delegate_ askForSettings];
4454 - (NSString *) leftButtonTitle {
4458 - (NSString *) _rightButtonTitle {
4465 @interface WebView (Cydia)
4466 - (void) setScriptDebugDelegate:(id)delegate;
4467 - (void) _setFormDelegate:(id)delegate;
4468 - (void) _setUIKitDelegate:(id)delegate;
4469 - (void) setWebMailDelegate:(id)delegate;
4470 - (void) _setLayoutInterval:(float)interval;
4473 /* Indirect Delegate {{{ */
4474 @interface IndirectDelegate : NSProxy {
4475 _transient volatile id delegate_;
4478 - (void) setDelegate:(id)delegate;
4479 - (id) initWithDelegate:(id)delegate;
4482 @implementation IndirectDelegate
4484 - (void) setDelegate:(id)delegate {
4485 delegate_ = delegate;
4488 - (id) initWithDelegate:(id)delegate {
4489 delegate_ = delegate;
4493 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4494 if (delegate_ != nil)
4495 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4497 // XXX: I fucking hate Apple so very very bad
4498 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4501 - (void) forwardInvocation:(NSInvocation *)inv {
4502 SEL sel = [inv selector];
4503 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4504 [inv invokeWithTarget:delegate_];
4509 /* Browser Implementation {{{ */
4510 @implementation BrowserView
4513 WebView *webview = [webview_ webView];
4514 [webview setFrameLoadDelegate:nil];
4515 [webview setResourceLoadDelegate:nil];
4516 [webview setUIDelegate:nil];
4517 [webview setScriptDebugDelegate:nil];
4518 [webview setPolicyDelegate:nil];
4520 [webview setDownloadDelegate:nil];
4522 [webview _setFormDelegate:nil];
4523 [webview _setUIKitDelegate:nil];
4524 [webview setWebMailDelegate:nil];
4525 [webview setEditingDelegate:nil];
4527 [webview_ setDelegate:nil];
4528 [webview_ setGestureDelegate:nil];
4530 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4535 [webview_ removeFromSuperview];
4536 [Documents_ addObject:[webview_ autorelease]];
4541 [indirect_ setDelegate:nil];
4542 [indirect_ release];
4544 [scroller_ setDelegate:nil];
4546 [scroller_ release];
4548 [indicator_ release];
4554 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4555 [self loadRequest:[NSURLRequest
4558 timeoutInterval:30.0
4562 - (void) loadURL:(NSURL *)url {
4563 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4566 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4567 NSMutableURLRequest *copy = [request mutableCopy];
4569 if (Machine_ != NULL)
4570 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4571 if (UniqueID_ != nil)
4572 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4575 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4580 - (void) loadRequest:(NSURLRequest *)request {
4582 [webview_ loadRequest:request];
4585 - (void) reloadURL {
4586 if ([urls_ count] == 0)
4588 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4589 [urls_ removeLastObject];
4590 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4593 - (WebView *) webView {
4594 return [webview_ webView];
4597 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4598 [scroller_ setContentSize:frame.size];
4601 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4602 [self view:sender didSetFrame:frame];
4605 - (void) pushPage:(RVPage *)page {
4606 [self setBackButtonTitle:title_];
4607 [page setDelegate:delegate_];
4608 [book_ pushPage:page];
4611 - (BOOL) getSpecial:(NSURL *)url {
4612 NSString *href([url absoluteString]);
4613 NSString *scheme([[url scheme] lowercaseString]);
4617 if ([href hasPrefix:@"apptapp://package/"])
4618 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4619 else if ([scheme isEqualToString:@"cydia"])
4620 page = [delegate_ pageForURL:url hasTag:NULL];
4621 else if (![scheme isEqualToString:@"apptapp"])
4625 [self pushPage:page];
4629 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4630 [window setValue:delegate_ forKey:@"cydia"];
4633 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4634 if (NSURL *url = [request URL]) {
4635 if (![self getSpecial:url]) {
4636 NSString *scheme([[url scheme] lowercaseString]);
4637 if ([scheme isEqualToString:@"mailto"])
4638 [delegate_ openMailToURL:url];
4647 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4648 NSURL *url([request URL]);
4650 if (url == nil) use: {
4655 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4658 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4659 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4662 [UIApp openURL:url];
4668 int store(_not(int));
4669 if (NSURL *itms = [url itmsURL:&store]) {
4671 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4672 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4679 NSString *scheme([[url scheme] lowercaseString]);
4681 if ([scheme isEqualToString:@"tel"]) {
4682 // XXX: intelligence
4686 if ([scheme isEqualToString:@"mailto"]) {
4687 [delegate_ openMailToURL:url];
4691 if ([self getSpecial:url])
4693 else if ([WebView _canHandleRequest:request])
4695 else if ([url isSpringboardHandledURL])
4701 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4702 //lprintf("Status:%s\n", [text UTF8String]);
4705 - (void) _pushPage {
4709 [book_ pushPage:self];
4712 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4713 NSURL *url = [request URL];
4714 if ([self getSpecial:url])
4717 return [self _addHeadersToRequest:request];
4720 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4721 [self setBackButtonTitle:title_];
4723 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4724 [browser setDelegate:delegate_];
4727 [browser loadRequest:[self _addHeadersToRequest:request]];
4728 [book_ pushPage:browser];
4731 return [browser webView];
4734 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4735 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4738 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4739 return [self _createWebViewWithRequest:request pushed:YES];
4742 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4743 if ([frame parentFrame] != nil)
4746 title_ = [title retain];
4747 [book_ reloadTitleForPage:self];
4750 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4751 if ([frame parentFrame] != nil)
4756 [indicator_ startAnimation];
4757 [self reloadButtons];
4759 if (title_ != nil) {
4764 [book_ reloadTitleForPage:self];
4766 WebView *webview = [webview_ webView];
4767 NSString *href = [webview mainFrameURL];
4768 [urls_ addObject:[NSURL URLWithString:href]];
4770 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4772 CGRect webrect = [scroller_ bounds];
4773 webrect.size.height = 0;
4774 [webview_ setFrame:webrect];
4777 - (void) _finishLoading {
4780 [indicator_ stopAnimation];
4781 [self reloadButtons];
4785 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4786 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4789 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4790 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4793 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4794 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4797 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4798 return [webview_ webView:sender didCommitLoadForFrame:frame];
4801 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4802 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4805 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4806 if ([frame parentFrame] == nil)
4807 [self _finishLoading];
4808 return [webview_ webView:sender didFinishLoadForFrame:frame];
4811 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4812 if ([frame parentFrame] != nil)
4814 [self _finishLoading];
4816 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4817 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4818 [[error localizedDescription] stringByAddingPercentEscapes]
4822 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
4824 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
4828 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4829 if ((self = [super initWithBook:book]) != nil) {
4830 database_ = database;
4833 struct CGRect bounds = [self bounds];
4835 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4836 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4837 [self addSubview:pinstripe];
4839 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4840 [self addSubview:scroller_];
4842 [scroller_ setScrollingEnabled:YES];
4843 [scroller_ setAdjustForContentSizeChange:YES];
4844 [scroller_ setClipsSubviews:YES];
4845 [scroller_ setAllowsRubberBanding:YES];
4846 [scroller_ setScrollDecelerationFactor:0.99];
4847 [scroller_ setDelegate:self];
4849 CGRect webrect = [scroller_ bounds];
4850 webrect.size.height = 0;
4855 webview_ = [Documents_ lastObject];
4856 if (webview_ != nil) {
4857 webview_ = [webview_ retain];
4858 webview = [webview_ webView];
4859 [Documents_ removeLastObject];
4860 [webview_ setFrame:webrect];
4865 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
4866 webview = [webview_ webView];
4868 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4870 [webview_ setAllowsMessaging:YES];
4872 [webview_ setTilingEnabled:YES];
4873 [webview_ setDrawsGrid:NO];
4874 [webview_ setLogsTilingChanges:NO];
4875 [webview_ setTileMinificationFilter:kCAFilterNearest];
4876 [webview_ setDetectsPhoneNumbers:NO];
4877 [webview_ setAutoresizes:YES];
4879 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4880 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4881 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4883 [webview_ _setDocumentType:0x4];
4885 [webview_ setZoomsFocusedFormControl:YES];
4886 [webview_ setContentsPosition:7];
4887 [webview_ setEnabledGestures:0xa];
4888 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4889 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4891 [webview_ setSmoothsFonts:YES];
4893 [webview _setUsesLoaderCache:YES];
4894 [webview setGroupName:@"Cydia"];
4895 //[webview _setLayoutInterval:0.5];
4898 [webview_ setDelegate:self];
4899 [webview_ setGestureDelegate:self];
4900 [scroller_ addSubview:webview_];
4902 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4904 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
4905 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
4906 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
4908 Package *package([database_ packageWithName:@"cydia"]);
4909 NSString *application = package == nil ? @"Cydia" : [NSString
4910 stringWithFormat:@"Cydia/%@",
4912 ]; [webview setApplicationNameForUserAgent:application];
4914 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
4916 [webview setFrameLoadDelegate:self];
4917 [webview setResourceLoadDelegate:indirect_];
4918 [webview setUIDelegate:self];
4919 [webview setScriptDebugDelegate:self];
4920 [webview setPolicyDelegate:self];
4922 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4924 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4925 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4926 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4930 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4931 [webview_ redrawScaledDocument];
4934 - (void) _rightButtonClicked {
4939 - (NSString *) _rightButtonTitle {
4943 - (NSString *) rightButtonTitle {
4944 return loading_ ? @"" : [self _rightButtonTitle];
4947 - (NSString *) title {
4948 return title_ == nil ? @"Loading" : title_;
4951 - (NSString *) backButtonTitle {
4955 - (void) setPageActive:(BOOL)active {
4957 [indicator_ removeFromSuperview];
4959 [[book_ navigationBar] addSubview:indicator_];
4962 - (void) resetViewAnimated:(BOOL)animated {
4965 - (void) setPushed:(bool)pushed {
4972 @interface CYBook : RVBook <
4975 _transient Database *database_;
4976 UINavigationBar *overlay_;
4977 UINavigationBar *underlay_;
4978 UIProgressIndicator *indicator_;
4979 UITextLabel *prompt_;
4980 UIProgressBar *progress_;
4981 UINavigationButton *cancel_;
4984 NSTimeInterval last_;
4987 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4993 /* Install View {{{ */
4994 @interface InstallView : RVPage {
4995 _transient Database *database_;
4996 NSMutableArray *sections_;
4997 NSMutableArray *filtered_;
4998 UITransitionView *transition_;
5004 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5005 - (void) reloadData;
5010 @implementation InstallView
5013 [list_ setDataSource:nil];
5014 [list_ setDelegate:nil];
5016 [sections_ release];
5017 [filtered_ release];
5018 [transition_ release];
5020 [accessory_ release];
5024 - (int) numberOfRowsInTable:(UITable *)table {
5025 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5028 - (float) table:(UITable *)table heightForRow:(int)row {
5032 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5034 reusing = [[[SectionCell alloc] init] autorelease];
5035 [(SectionCell *)reusing setSection:(editing_ ?
5036 [sections_ objectAtIndex:row] :
5037 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5038 ) editing:editing_];
5042 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5046 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5050 - (void) tableRowSelected:(NSNotification *)notification {
5051 int row = [[notification object] selectedRow];
5062 title = @"All Packages";
5064 section = [filtered_ objectAtIndex:(row - 1)];
5065 name = [section name];
5071 title = @"(No Section)";
5075 PackageTable *table = [[[PackageTable alloc]
5079 filter:@selector(isVisiblyUninstalledInSection:)
5083 [table setDelegate:delegate_];
5085 [book_ pushPage:table];
5088 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5089 if ((self = [super initWithBook:book]) != nil) {
5090 database_ = database;
5092 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5093 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5095 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5096 [self addSubview:transition_];
5098 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5099 [transition_ transition:0 toView:list_];
5101 UITableColumn *column = [[[UITableColumn alloc]
5102 initWithTitle:@"Name"
5104 width:[self frame].size.width
5107 [list_ setDataSource:self];
5108 [list_ setSeparatorStyle:1];
5109 [list_ addTableColumn:column];
5110 [list_ setDelegate:self];
5111 [list_ setReusesTableCells:YES];
5115 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5116 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5120 - (void) reloadData {
5121 NSArray *packages = [database_ packages];
5123 [sections_ removeAllObjects];
5124 [filtered_ removeAllObjects];
5126 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5127 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5129 for (size_t i(0); i != [packages count]; ++i) {
5130 Package *package([packages objectAtIndex:i]);
5131 NSString *name([package section]);
5134 Section *section([sections objectForKey:name]);
5135 if (section == nil) {
5136 section = [[[Section alloc] initWithName:name] autorelease];
5137 [sections setObject:section forKey:name];
5141 if ([package valid] && [package installed] == nil && [package visible])
5142 [filtered addObject:package];
5145 [sections_ addObjectsFromArray:[sections allValues]];
5146 [sections_ sortUsingSelector:@selector(compareByName:)];
5148 [filtered sortUsingSelector:@selector(compareBySection:)];
5150 Section *section = nil;
5151 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5152 Package *package = [filtered objectAtIndex:offset];
5153 NSString *name = [package section];
5155 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5156 section = name == nil ?
5157 [[[Section alloc] initWithName:nil] autorelease] :
5158 [sections objectForKey:name];
5159 [filtered_ addObject:section];
5162 [section addToCount];
5168 - (void) resetView {
5170 [self _rightButtonClicked];
5173 - (void) resetViewAnimated:(BOOL)animated {
5174 [list_ resetViewAnimated:animated];
5177 - (void) _rightButtonClicked {
5178 if ((editing_ = !editing_))
5181 [delegate_ updateData];
5184 [book_ reloadTitleForPage:self];
5185 [book_ reloadButtonsForPage:self];
5188 - (NSString *) title {
5189 return editing_ ? @"Section Visibility" : @"Install by Section";
5192 - (NSString *) backButtonTitle {
5196 - (NSString *) rightButtonTitle {
5197 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5200 - (UINavigationButtonStyle) rightButtonStyle {
5201 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5204 - (UIView *) accessoryView {
5210 /* Changes View {{{ */
5211 @interface ChangesView : RVPage {
5212 _transient Database *database_;
5213 NSMutableArray *packages_;
5214 NSMutableArray *sections_;
5215 UISectionList *list_;
5219 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5220 - (void) reloadData;
5224 @implementation ChangesView
5227 [[list_ table] setDelegate:nil];
5228 [list_ setDataSource:nil];
5230 [packages_ release];
5231 [sections_ release];
5236 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5237 return [sections_ count];
5240 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5241 return [[sections_ objectAtIndex:section] name];
5244 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5245 return [[sections_ objectAtIndex:section] row];
5248 - (int) numberOfRowsInTable:(UITable *)table {
5249 return [packages_ count];
5252 - (float) table:(UITable *)table heightForRow:(int)row {
5253 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5256 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5258 reusing = [[[PackageCell alloc] init] autorelease];
5259 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5263 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5267 - (void) tableRowSelected:(NSNotification *)notification {
5268 int row = [[notification object] selectedRow];
5271 Package *package = [packages_ objectAtIndex:row];
5272 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5273 [view setDelegate:delegate_];
5274 [view setPackage:package];
5275 [book_ pushPage:view];
5278 - (void) _leftButtonClicked {
5279 [(CYBook *)book_ update];
5280 [self reloadButtons];
5283 - (void) _rightButtonClicked {
5284 [delegate_ distUpgrade];
5287 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5288 if ((self = [super initWithBook:book]) != nil) {
5289 database_ = database;
5291 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5292 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5294 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5295 [self addSubview:list_];
5297 [list_ setShouldHideHeaderInShortLists:NO];
5298 [list_ setDataSource:self];
5299 //[list_ setSectionListStyle:1];
5301 UITableColumn *column = [[[UITableColumn alloc]
5302 initWithTitle:@"Name"
5304 width:[self frame].size.width
5307 UITable *table = [list_ table];
5308 [table setSeparatorStyle:1];
5309 [table addTableColumn:column];
5310 [table setDelegate:self];
5311 [table setReusesTableCells:YES];
5315 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5316 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5320 - (void) reloadData {
5321 NSArray *packages = [database_ packages];
5323 [packages_ removeAllObjects];
5324 [sections_ removeAllObjects];
5326 for (size_t i(0); i != [packages count]; ++i) {
5327 Package *package([packages objectAtIndex:i]);
5330 [package installed] == nil && [package valid] && [package visible] ||
5331 [package upgradableAndEssential:NO]
5333 [packages_ addObject:package];
5336 [packages_ sortUsingSelector:@selector(compareForChanges:)];
5338 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5339 Section *section = nil;
5342 bool unseens = false;
5344 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5346 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5347 Package *package = [packages_ objectAtIndex:offset];
5349 if ([package upgradableAndEssential:YES]) {
5351 [upgradable addToCount];
5354 NSDate *seen = [package seen];
5359 name = [@"n/a ?" retain];
5361 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
5364 if (section == nil || ![[section name] isEqualToString:name]) {
5365 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5366 [sections_ addObject:section];
5370 [section addToCount];
5374 CFRelease(formatter);
5377 Section *last = [sections_ lastObject];
5378 size_t count = [last count];
5379 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5380 [sections_ removeLastObject];
5384 [sections_ insertObject:upgradable atIndex:0];
5387 [self reloadButtons];
5390 - (void) resetViewAnimated:(BOOL)animated {
5391 [list_ resetViewAnimated:animated];
5394 - (NSString *) leftButtonTitle {
5395 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5398 - (NSString *) rightButtonTitle {
5399 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5402 - (NSString *) title {
5408 /* Search View {{{ */
5409 @protocol SearchViewDelegate
5410 - (void) showKeyboard:(BOOL)show;
5413 @interface SearchView : RVPage {
5415 UISearchField *field_;
5416 UITransitionView *transition_;
5417 PackageTable *table_;
5418 UIPreferencesTable *advanced_;
5424 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5425 - (void) reloadData;
5429 @implementation SearchView
5432 [field_ setDelegate:nil];
5434 [accessory_ release];
5436 [transition_ release];
5438 [advanced_ release];
5443 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5447 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5449 case 0: return @"Advanced Search (Coming Soon!)";
5451 default: _assert(false);
5455 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5459 default: _assert(false);
5463 - (void) _showKeyboard:(BOOL)show {
5464 CGSize keysize = [UIKeyboard defaultSize];
5465 CGRect keydown = [book_ pageBounds];
5466 CGRect keyup = keydown;
5467 keyup.size.height -= keysize.height - ButtonBarHeight_;
5469 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5471 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5472 [animation setSignificantRectFields:8];
5475 [animation setStartFrame:keydown];
5476 [animation setEndFrame:keyup];
5478 [animation setStartFrame:keyup];
5479 [animation setEndFrame:keydown];
5482 UIAnimator *animator = [UIAnimator sharedAnimator];
5485 addAnimations:[NSArray arrayWithObjects:animation, nil]
5486 withDuration:(KeyboardTime_ - delay)
5491 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5493 [delegate_ showKeyboard:show];
5496 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5497 [self _showKeyboard:YES];
5500 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5501 [self _showKeyboard:NO];
5504 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5506 NSString *text([field_ text]);
5507 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5513 - (void) textFieldClearButtonPressed:(UITextField *)field {
5517 - (void) keyboardInputShouldDelete:(id)input {
5521 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5522 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5526 [field_ resignFirstResponder];
5531 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5532 if ((self = [super initWithBook:book]) != nil) {
5533 CGRect pageBounds = [book_ pageBounds];
5535 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5536 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5537 [self addSubview:pinstripe];*/
5539 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5540 [self addSubview:transition_];
5542 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5544 [advanced_ setReusesTableCells:YES];
5545 [advanced_ setDataSource:self];
5546 [advanced_ reloadData];
5548 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5549 CGColor dimmed(space_, 0, 0, 0, 0.5);
5550 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5552 table_ = [[PackageTable alloc]
5556 filter:@selector(isUnfilteredAndSearchedForBy:)
5560 [table_ setShouldHideHeaderInShortLists:NO];
5561 [transition_ transition:0 toView:table_];
5570 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5577 [self bounds].size.width - area.origin.x - 18;
5579 area.size.height = [UISearchField defaultHeight];
5581 field_ = [[UISearchField alloc] initWithFrame:area];
5583 UIFont *font = [UIFont systemFontOfSize:16];
5584 [field_ setFont:font];
5586 [field_ setPlaceholder:@"Package Names & Descriptions"];
5587 [field_ setDelegate:self];
5589 [field_ setPaddingTop:5];
5591 UITextInputTraits *traits = [field_ textInputTraits];
5592 [traits setAutocapitalizationType:0];
5593 [traits setAutocorrectionType:1];
5594 [traits setReturnKeyType:6];
5596 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5598 accessory_ = [[UIView alloc] initWithFrame:accrect];
5599 [accessory_ addSubview:field_];
5601 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5602 [configure setShowPressFeedback:YES];
5603 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5604 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5605 [accessory_ addSubview:configure];*/
5607 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5608 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5614 LKAnimation *animation = [LKTransition animation];
5615 [animation setType:@"oglFlip"];
5616 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5617 [animation setFillMode:@"extended"];
5618 [animation setTransitionFlags:3];
5619 [animation setDuration:10];
5620 [animation setSpeed:0.35];
5621 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5622 [[transition_ _layer] addAnimation:animation forKey:0];
5623 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5624 flipped_ = !flipped_;
5628 - (void) configurePushed {
5629 [field_ resignFirstResponder];
5633 - (void) resetViewAnimated:(BOOL)animated {
5636 [table_ resetViewAnimated:animated];
5639 - (void) _reloadData {
5642 - (void) reloadData {
5645 [table_ setObject:[field_ text]];
5646 [table_ reloadData];
5647 [table_ resetCursor];
5650 - (UIView *) accessoryView {
5654 - (NSString *) title {
5658 - (NSString *) backButtonTitle {
5662 - (void) setDelegate:(id)delegate {
5663 [table_ setDelegate:delegate];
5664 [super setDelegate:delegate];
5670 @implementation CYBook
5674 [indicator_ release];
5676 [progress_ release];
5681 - (NSString *) getTitleForPage:(RVPage *)page {
5682 return Simplify([super getTitleForPage:page]);
5690 [UIView beginAnimations:nil context:NULL];
5692 CGRect ovrframe = [overlay_ frame];
5693 ovrframe.origin.y = 0;
5694 [overlay_ setFrame:ovrframe];
5696 CGRect barframe = [navbar_ frame];
5697 barframe.origin.y += ovrframe.size.height;
5698 [navbar_ setFrame:barframe];
5700 CGRect trnframe = [transition_ frame];
5701 trnframe.origin.y += ovrframe.size.height;
5702 trnframe.size.height -= ovrframe.size.height;
5703 [transition_ setFrame:trnframe];
5705 [UIView endAnimations];
5707 [indicator_ startAnimation];
5708 [prompt_ setText:@"Updating Database"];
5709 [progress_ setProgress:0];
5712 last_ = [NSDate timeIntervalSinceReferenceDate];
5714 [overlay_ addSubview:cancel_];
5717 detachNewThreadSelector:@selector(_update)
5726 [indicator_ stopAnimation];
5728 [UIView beginAnimations:nil context:NULL];
5730 CGRect ovrframe = [overlay_ frame];
5731 ovrframe.origin.y = -ovrframe.size.height;
5732 [overlay_ setFrame:ovrframe];
5734 CGRect barframe = [navbar_ frame];
5735 barframe.origin.y -= ovrframe.size.height;
5736 [navbar_ setFrame:barframe];
5738 CGRect trnframe = [transition_ frame];
5739 trnframe.origin.y -= ovrframe.size.height;
5740 trnframe.size.height += ovrframe.size.height;
5741 [transition_ setFrame:trnframe];
5743 [UIView commitAnimations];
5745 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5748 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5749 if ((self = [super initWithFrame:frame]) != nil) {
5750 database_ = database;
5752 CGRect ovrrect = [navbar_ bounds];
5753 ovrrect.size.height = [UINavigationBar defaultSize].height;
5754 ovrrect.origin.y = -ovrrect.size.height;
5756 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5757 [self addSubview:overlay_];
5759 ovrrect.origin.y = frame.size.height;
5760 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5761 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5762 [self addSubview:underlay_];
5764 [overlay_ setBarStyle:1];
5765 [underlay_ setBarStyle:1];
5767 int barstyle = [overlay_ _barStyle:NO];
5768 bool ugly = barstyle == 0;
5770 UIProgressIndicatorStyle style = ugly ?
5771 UIProgressIndicatorStyleMediumBrown :
5772 UIProgressIndicatorStyleMediumWhite;
5774 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5775 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5776 CGRect indrect = {{indoffset, indoffset}, indsize};
5778 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5779 [indicator_ setStyle:style];
5780 [overlay_ addSubview:indicator_];
5782 CGSize prmsize = {215, indsize.height + 4};
5785 indoffset * 2 + indsize.width,
5789 unsigned(ovrrect.size.height - prmsize.height) / 2
5792 UIFont *font = [UIFont systemFontOfSize:15];
5794 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5796 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5797 [prompt_ setBackgroundColor:[UIColor clearColor]];
5798 [prompt_ setFont:font];
5800 [overlay_ addSubview:prompt_];
5802 CGSize prgsize = {75, 100};
5805 ovrrect.size.width - prgsize.width - 10,
5806 (ovrrect.size.height - prgsize.height) / 2
5809 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5810 [progress_ setStyle:0];
5811 [overlay_ addSubview:progress_];
5813 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5814 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5816 CGRect frame = [cancel_ frame];
5817 frame.size.width = 65;
5818 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5819 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5820 [cancel_ setFrame:frame];
5822 [cancel_ setBarStyle:barstyle];
5826 - (void) _onCancel {
5828 [cancel_ removeFromSuperview];
5831 - (void) _update { _pooled
5833 status.setDelegate(self);
5835 [database_ updateWithStatus:status];
5838 performSelectorOnMainThread:@selector(_update_)
5844 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5845 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5848 - (void) setProgressTitle:(NSString *)title {
5850 performSelectorOnMainThread:@selector(_setProgressTitle:)
5856 - (void) setProgressPercent:(float)percent {
5858 performSelectorOnMainThread:@selector(_setProgressPercent:)
5859 withObject:[NSNumber numberWithFloat:percent]
5864 - (void) startProgress {
5867 - (void) addProgressOutput:(NSString *)output {
5869 performSelectorOnMainThread:@selector(_addProgressOutput:)
5875 - (bool) isCancelling:(size_t)received {
5876 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5877 if (received_ != received) {
5878 received_ = received;
5880 } else if (now - last_ > 15)
5885 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5889 - (void) _setProgressTitle:(NSString *)title {
5890 [prompt_ setText:title];
5893 - (void) _setProgressPercent:(NSNumber *)percent {
5894 [progress_ setProgress:[percent floatValue]];
5897 - (void) _addProgressOutput:(NSString *)output {
5902 @interface Cydia : UIApplication <
5903 ConfirmationViewDelegate,
5904 ProgressViewDelegate,
5913 UIToolbar *buttonbar_;
5917 NSMutableArray *essential_;
5918 NSMutableArray *broken_;
5920 Database *database_;
5921 ProgressView *progress_;
5925 UIKeyboard *keyboard_;
5926 UIProgressHUD *hud_;
5928 InstallView *install_;
5929 ChangesView *changes_;
5930 ManageView *manage_;
5931 SearchView *search_;
5936 @implementation Cydia
5939 if ([broken_ count] != 0) {
5940 int count = [broken_ count];
5942 UIActionSheet *sheet = [[[UIActionSheet alloc]
5943 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5944 buttons:[NSArray arrayWithObjects:
5946 @"Ignore (Temporary)",
5948 defaultButtonIndex:0
5953 [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."];
5954 [sheet popupAlertAnimated:YES];
5955 } else if (!Ignored_ && [essential_ count] != 0) {
5956 int count = [essential_ count];
5958 UIActionSheet *sheet = [[[UIActionSheet alloc]
5959 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5960 buttons:[NSArray arrayWithObjects:
5961 @"Upgrade Essential",
5962 @"Upgrade Everything",
5963 @"Ignore (Temporary)",
5965 defaultButtonIndex:0
5970 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5971 [sheet popupAlertAnimated:YES];
5975 - (void) _reloadData {
5976 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5977 [hud setText:@"Reloading Data"];
5978 [overlay_ addSubview:hud];
5981 [database_ reloadData];
5985 [essential_ removeAllObjects];
5986 [broken_ removeAllObjects];
5988 NSArray *packages = [database_ packages];
5989 for (Package *package in packages) {
5991 [broken_ addObject:package];
5992 if ([package upgradableAndEssential:NO]) {
5993 if ([package essential])
5994 [essential_ addObject:package];
6000 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6001 [buttonbar_ setBadgeValue:badge forButton:3];
6002 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6003 [buttonbar_ setBadgeAnimated:YES forButton:3];
6004 [self setApplicationBadge:badge];
6006 [buttonbar_ setBadgeValue:nil forButton:3];
6007 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6008 [buttonbar_ setBadgeAnimated:NO forButton:3];
6009 [self removeApplicationBadge];
6015 if ([packages count] == 0);
6027 [hud removeFromSuperview];*/
6030 - (void) _saveConfig {
6032 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6037 - (void) updateData {
6040 /* XXX: this is just stupid */
6042 [install_ reloadData];
6044 [changes_ reloadData];
6046 [search_ reloadData];
6056 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6057 _assert(file != NULL);
6059 NSArray *keys = [Sources_ allKeys];
6061 for (int i(0), e([keys count]); i != e; ++i) {
6062 NSString *key = [keys objectAtIndex:i];
6063 NSDictionary *source = [Sources_ objectForKey:key];
6065 fprintf(file, "%s %s %s\n",
6066 [[source objectForKey:@"Type"] UTF8String],
6067 [[source objectForKey:@"URI"] UTF8String],
6068 [[source objectForKey:@"Distribution"] UTF8String]
6077 detachNewThreadSelector:@selector(update_)
6080 title:@"Updating Sources"
6084 - (void) reloadData {
6085 @synchronized (self) {
6086 if (confirm_ == nil)
6092 pkgProblemResolver *resolver = [database_ resolver];
6094 resolver->InstallProtect();
6095 if (!resolver->Resolve(true))
6100 [database_ prepare];
6102 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6103 [confirm_ setDelegate:self];
6105 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6106 [page setDelegate:self];
6108 [confirm_ setPage:page];
6109 [underlay_ popSubview:confirm_];
6112 - (void) installPackage:(Package *)package {
6113 @synchronized (self) {
6120 - (void) removePackage:(Package *)package {
6121 @synchronized (self) {
6128 - (void) distUpgrade {
6129 @synchronized (self) {
6130 [database_ upgrade];
6136 @synchronized (self) {
6138 if (confirm_ != nil) {
6146 [overlay_ removeFromSuperview];
6150 detachNewThreadSelector:@selector(perform)
6157 - (void) bootstrap_ {
6159 [database_ upgrade];
6160 [database_ prepare];
6161 [database_ perform];
6164 - (void) bootstrap {
6166 detachNewThreadSelector:@selector(bootstrap_)
6169 title:@"Bootstrap Install"
6173 - (void) progressViewIsComplete:(ProgressView *)progress {
6174 if (confirm_ != nil) {
6175 [underlay_ addSubview:overlay_];
6176 [confirm_ popFromSuperviewAnimated:NO];
6182 - (void) setPage:(RVPage *)page {
6183 [page resetViewAnimated:NO];
6184 [page setDelegate:self];
6185 [book_ setPage:page];
6188 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6189 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
6190 [browser loadURL:url];
6194 - (void) _setHomePage {
6195 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6198 - (void) buttonBarItemTapped:(id)sender {
6199 unsigned tag = [sender tag];
6201 [book_ resetViewAnimated:YES];
6203 } else if (tag_ == 2 && tag != 2)
6204 [install_ resetView];
6207 case 1: [self _setHomePage]; break;
6209 case 2: [self setPage:install_]; break;
6210 case 3: [self setPage:changes_]; break;
6211 case 4: [self setPage:manage_]; break;
6212 case 5: [self setPage:search_]; break;
6214 default: _assert(false);
6220 - (void) applicationWillSuspend {
6222 [super applicationWillSuspend];
6225 - (void) askForSettings {
6226 UIActionSheet *role = [[[UIActionSheet alloc]
6227 initWithTitle:@"Who Are You?"
6228 buttons:[NSArray arrayWithObjects:
6229 @"User (Graphical Only)",
6230 @"Hacker (+ Command Line)",
6231 @"Developer (No Filters)",
6233 defaultButtonIndex:-1
6238 [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."];
6239 [role popupAlertAnimated:YES];
6244 [self setStatusBarShowsProgress:NO];
6247 [hud_ removeFromSuperview];
6251 pid_t pid = ExecFork();
6253 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6254 perror("launchctl stop");
6261 [self askForSettings];
6265 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6267 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6268 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6269 0, 0, screenrect.size.width, screenrect.size.height - 48
6270 ) database:database_];
6272 [book_ setDelegate:self];
6274 [overlay_ addSubview:book_];
6276 NSArray *buttonitems = [NSArray arrayWithObjects:
6277 [NSDictionary dictionaryWithObjectsAndKeys:
6278 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6279 @"home-up.png", kUIButtonBarButtonInfo,
6280 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6281 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6282 self, kUIButtonBarButtonTarget,
6283 @"Home", kUIButtonBarButtonTitle,
6284 @"0", kUIButtonBarButtonType,
6287 [NSDictionary dictionaryWithObjectsAndKeys:
6288 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6289 @"install-up.png", kUIButtonBarButtonInfo,
6290 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6291 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6292 self, kUIButtonBarButtonTarget,
6293 @"Sections", kUIButtonBarButtonTitle,
6294 @"0", kUIButtonBarButtonType,
6297 [NSDictionary dictionaryWithObjectsAndKeys:
6298 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6299 @"changes-up.png", kUIButtonBarButtonInfo,
6300 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6301 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6302 self, kUIButtonBarButtonTarget,
6303 @"Changes", kUIButtonBarButtonTitle,
6304 @"0", kUIButtonBarButtonType,
6307 [NSDictionary dictionaryWithObjectsAndKeys:
6308 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6309 @"manage-up.png", kUIButtonBarButtonInfo,
6310 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6311 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6312 self, kUIButtonBarButtonTarget,
6313 @"Manage", kUIButtonBarButtonTitle,
6314 @"0", kUIButtonBarButtonType,
6317 [NSDictionary dictionaryWithObjectsAndKeys:
6318 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6319 @"search-up.png", kUIButtonBarButtonInfo,
6320 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6321 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6322 self, kUIButtonBarButtonTarget,
6323 @"Search", kUIButtonBarButtonTitle,
6324 @"0", kUIButtonBarButtonType,
6328 buttonbar_ = [[UIToolbar alloc]
6330 withFrame:CGRectMake(
6331 0, screenrect.size.height - ButtonBarHeight_,
6332 screenrect.size.width, ButtonBarHeight_
6334 withItemList:buttonitems
6337 [buttonbar_ setDelegate:self];
6338 [buttonbar_ setBarStyle:1];
6339 [buttonbar_ setButtonBarTrackingMode:2];
6341 int buttons[5] = {1, 2, 3, 4, 5};
6342 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6343 [buttonbar_ showButtonGroup:0 withDuration:0];
6345 for (int i = 0; i != 5; ++i)
6346 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6347 i * 64 + 2, 1, 60, ButtonBarHeight_
6350 [buttonbar_ showSelectionForButton:1];
6351 [overlay_ addSubview:buttonbar_];
6353 [UIKeyboard initImplementationNow];
6354 CGSize keysize = [UIKeyboard defaultSize];
6355 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6356 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6357 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6358 [overlay_ addSubview:keyboard_];
6360 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6361 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6362 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6364 manage_ = (ManageView *) [[self
6365 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6366 withClass:[ManageView class]
6370 [underlay_ addSubview:overlay_];
6377 [self _setHomePage];
6380 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6381 NSString *context = [sheet context];
6382 if ([context isEqualToString:@"fixhalf"])
6385 @synchronized (self) {
6386 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6387 Package *broken = [broken_ objectAtIndex:i];
6390 NSString *id = [broken id];
6391 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6392 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6393 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6394 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6403 [broken_ removeAllObjects];
6410 else if ([context isEqualToString:@"role"]) {
6412 case 1: Role_ = @"User"; break;
6413 case 2: Role_ = @"Hacker"; break;
6414 case 3: Role_ = @"Developer"; break;
6421 bool reset = Settings_ != nil;
6423 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6427 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6435 } else if ([context isEqualToString:@"upgrade"])
6438 @synchronized (self) {
6439 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6440 Package *essential = [essential_ objectAtIndex:i];
6441 [essential install];
6464 - (void) reorganize { _pooled
6465 system("/usr/libexec/cydia/free.sh");
6466 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6469 - (void) applicationSuspend:(__GSEvent *)event {
6470 if (hud_ == nil && ![progress_ isRunning])
6471 [super applicationSuspend:event];
6474 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6476 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6479 - (void) _setSuspended:(BOOL)value {
6481 [super _setSuspended:value];
6484 - (UIProgressHUD *) addProgressHUD {
6485 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6487 [underlay_ addSubview:hud];
6491 - (void) openMailToURL:(NSURL *)url {
6492 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6495 - (RVPage *) pageForPackage:(NSString *)name {
6496 if (Package *package = [database_ packageWithName:name]) {
6497 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6498 [view setPackage:package];
6501 UIActionSheet *sheet = [[[UIActionSheet alloc]
6502 initWithTitle:@"Cannot Locate Package"
6503 buttons:[NSArray arrayWithObjects:@"Close", nil]
6504 defaultButtonIndex:0
6509 [sheet setBodyText:[NSString stringWithFormat:
6510 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6513 [sheet popupAlertAnimated:YES];
6518 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6519 NSString *href = [url absoluteString];
6524 if ([href isEqualToString:@"cydia://add-source"])
6525 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6526 else if ([href isEqualToString:@"cydia://sources"])
6527 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6528 else if ([href isEqualToString:@"cydia://packages"])
6529 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6530 else if ([href hasPrefix:@"cydia://url/"])
6531 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6532 else if ([href hasPrefix:@"cydia://launch/"])
6533 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6534 else if ([href hasPrefix:@"cydia://package/"])
6535 return [self pageForPackage:[href substringFromIndex:16]];
6536 else if ([href hasPrefix:@"cydia://files/"]) {
6537 NSString *name = [href substringFromIndex:14];
6539 if (Package *package = [database_ packageWithName:name]) {
6540 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6541 [files setPackage:package];
6549 - (void) applicationOpenURL:(NSURL *)url {
6550 [super applicationOpenURL:url];
6552 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6553 [self setPage:page];
6554 [buttonbar_ showSelectionForButton:tag];
6559 - (void) applicationDidFinishLaunching:(id)unused {
6560 Font12_ = [[UIFont systemFontOfSize:12] retain];
6561 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6562 Font14_ = [[UIFont systemFontOfSize:14] retain];
6563 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6564 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6566 _assert(pkgInitConfig(*_config));
6567 _assert(pkgInitSystem(*_config, _system));
6571 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6572 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6574 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6575 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6577 [window_ orderFront:self];
6578 [window_ makeKey:self];
6579 [window_ setHidden:NO];
6581 database_ = [[Database alloc] init];
6582 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6583 [database_ setDelegate:progress_];
6584 [window_ setContentView:progress_];
6586 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6587 [progress_ setContentView:underlay_];
6589 [progress_ resetView];
6592 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6593 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6594 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6595 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6596 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6597 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6598 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6600 [self setIdleTimerDisabled:YES];
6602 hud_ = [self addProgressHUD];
6603 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6605 [self setStatusBarShowsProgress:YES];
6608 detachNewThreadSelector:@selector(reorganize)
6616 /* Web Scripting {{{ */
6617 + (NSString *) webScriptNameForSelector:(SEL)selector {
6618 if (selector == @selector(supports:))
6623 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
6624 NSLog(@"exc:%s", sel_getName(selector));
6625 return selector != @selector(supports:);
6628 - (BOOL) supports:(NSString *)feature {
6629 return [feature isEqualToString:@"window.open"];
6633 - (void) showKeyboard:(BOOL)show {
6634 CGSize keysize = [UIKeyboard defaultSize];
6635 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6636 CGRect keyup = keydown;
6637 keyup.origin.y -= keysize.height;
6639 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6640 [animation setSignificantRectFields:2];
6643 [animation setStartFrame:keydown];
6644 [animation setEndFrame:keyup];
6645 [keyboard_ activate];
6647 [animation setStartFrame:keyup];
6648 [animation setEndFrame:keydown];
6649 [keyboard_ deactivate];
6652 [[UIAnimator sharedAnimator]
6653 addAnimations:[NSArray arrayWithObjects:animation, nil]
6654 withDuration:KeyboardTime_
6659 - (void) slideUp:(UIActionSheet *)alert {
6661 [alert presentSheetFromButtonBar:buttonbar_];
6663 [alert presentSheetInView:overlay_];
6668 void AddPreferences(NSString *plist) { _pooled
6669 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6670 _assert(settings != NULL);
6671 NSMutableArray *items = [settings objectForKey:@"items"];
6675 for (size_t i(0); i != [items count]; ++i) {
6676 NSMutableDictionary *item([items objectAtIndex:i]);
6677 NSString *label = [item objectForKey:@"label"];
6678 if (label != nil && [label isEqualToString:@"Cydia"]) {
6685 for (size_t i(0); i != [items count]; ++i) {
6686 NSDictionary *item([items objectAtIndex:i]);
6687 NSString *label = [item objectForKey:@"label"];
6688 if (label != nil && [label isEqualToString:@"General"]) {
6689 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6690 @"CydiaSettings", @"bundle",
6691 @"PSLinkCell", @"cell",
6692 [NSNumber numberWithBool:YES], @"hasIcon",
6693 [NSNumber numberWithBool:YES], @"isController",
6695 nil] atIndex:(i + 1)];
6701 _assert([settings writeToFile:plist atomically:YES] == YES);
6706 id Alloc_(id self, SEL selector) {
6707 id object = alloc_(self, selector);
6708 lprintf("[%s]A-%p\n", self->isa->name, object);
6713 id Dealloc_(id self, SEL selector) {
6714 id object = dealloc_(self, selector);
6715 lprintf("[%s]D-%p\n", self->isa->name, object);
6719 int main(int argc, char *argv[]) { _pooled
6720 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6722 App_ = [[NSBundle mainBundle] bundlePath];
6723 Home_ = NSHomeDirectory();
6724 Locale_ = CFLocaleCopyCurrent();
6727 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6728 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6729 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6730 Sounds_Keyboard_ = [keyboard boolValue];
6736 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6737 _assert(errno == ENOENT);
6738 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6739 _assert(errno == ENOENT);
6741 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6742 alloc_ = alloc->method_imp;
6743 alloc->method_imp = (IMP) &Alloc_;*/
6745 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6746 dealloc_ = dealloc->method_imp;
6747 dealloc->method_imp = (IMP) &Dealloc_;*/
6752 size = sizeof(maxproc);
6753 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6754 perror("sysctlbyname(\"kern.maxproc\", ?)");
6755 else if (maxproc < 64) {
6757 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6758 perror("sysctlbyname(\"kern.maxproc\", #)");
6761 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6762 char *machine = new char[size];
6763 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6764 perror("sysctlbyname(\"hw.machine\", ?)");
6768 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6770 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6771 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6773 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6774 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6776 Settings_ = [Metadata_ objectForKey:@"Settings"];
6778 Packages_ = [Metadata_ objectForKey:@"Packages"];
6779 Sections_ = [Metadata_ objectForKey:@"Sections"];
6780 Sources_ = [Metadata_ objectForKey:@"Sources"];
6783 if (Settings_ != nil)
6784 Role_ = [Settings_ objectForKey:@"Role"];
6786 if (Packages_ == nil) {
6787 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6788 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6791 if (Sections_ == nil) {
6792 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6793 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6796 if (Sources_ == nil) {
6797 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6798 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6802 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
6805 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6806 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6808 if (access("/User", F_OK) != 0)
6809 system("/usr/libexec/cydia/firmware.sh");
6811 _assert([[NSFileManager defaultManager]
6812 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
6813 withIntermediateDirectories:YES
6818 space_ = CGColorSpaceCreateDeviceRGB();
6820 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6821 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6822 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6823 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
6824 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6825 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6827 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6829 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6831 UIApplicationUseLegacyEvents(YES);
6832 UIKeyboardDisableAutomaticAppearance();
6834 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6836 CGColorSpaceRelease(space_);