1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 // XXX: wtf/FastMalloc.h... wtf?
39 #define USE_SYSTEM_MALLOC 1
41 /* #include Directives {{{ */
42 #import "UICaboodle.h"
44 #include <objc/objc.h>
45 #include <objc/runtime.h>
47 #include <CoreGraphics/CoreGraphics.h>
48 #include <GraphicsServices/GraphicsServices.h>
49 #include <Foundation/Foundation.h>
51 #import <QuartzCore/CALayer.h>
53 #import <UIKit/UIKit.h>
56 #import <MessageUI/MailComposeController.h>
61 #include <ext/stdio_filebuf.h>
63 #include <apt-pkg/acquire.h>
64 #include <apt-pkg/acquire-item.h>
65 #include <apt-pkg/algorithms.h>
66 #include <apt-pkg/cachefile.h>
67 #include <apt-pkg/clean.h>
68 #include <apt-pkg/configuration.h>
69 #include <apt-pkg/debmetaindex.h>
70 #include <apt-pkg/error.h>
71 #include <apt-pkg/init.h>
72 #include <apt-pkg/mmap.h>
73 #include <apt-pkg/pkgrecords.h>
74 #include <apt-pkg/sha1.h>
75 #include <apt-pkg/sourcelist.h>
76 #include <apt-pkg/sptr.h>
77 #include <apt-pkg/strutl.h>
79 #include <sys/types.h>
81 #include <sys/sysctl.h>
82 #include <sys/param.h>
83 #include <sys/mount.h>
89 #include <mach-o/nlist.h>
99 #import "BrowserView.h"
100 #import "ResetView.h"
102 #import "substrate.h"
105 //#define _finline __attribute__((force_inline))
106 #define _finline inline
111 #define _limit(count) do { \
112 static size_t _count(0); \
113 if (++_count == count) \
117 static uint64_t profile_;
119 #define _timestamp ({ \
121 gettimeofday(&tv, NULL); \
122 tv.tv_sec * 1000000 + tv.tv_usec; \
125 /* Objective-C Handle<> {{{ */
126 template <typename Type_>
128 typedef _H<Type_> This_;
133 _finline void Retain_() {
138 _finline void Clear_() {
144 _finline _H(Type_ *value = NULL, bool mended = false) :
155 _finline This_ &operator =(Type_ *value) {
156 if (value_ != value) {
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
167 void NSLogPoint(const char *fix, const CGPoint &point) {
168 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
171 void NSLogRect(const char *fix, const CGRect &rect) {
172 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
175 /* NSForcedOrderingSearch doesn't work on the iPhone */
176 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
177 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
178 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
180 /* iPhoneOS 2.0 Compatibility {{{ */
182 @interface UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size;
186 @implementation UITextView (iPhoneOS)
188 - (void) setTextSize:(float)size {
189 [self setFont:[[self font] fontWithSize:size]];
196 extern NSString * const kCAFilterNearest;
198 /* Information Dictionaries {{{ */
199 @interface NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info;
203 @implementation NSMutableArray (Cydia)
205 - (void) addInfoDictionary:(NSDictionary *)info {
206 [self addObject:info];
211 @interface NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info;
215 @implementation NSMutableDictionary (Cydia)
217 - (void) addInfoDictionary:(NSDictionary *)info {
218 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
219 [self setObject:info forKey:bundle];
224 /* Pop Transitions {{{ */
225 @interface PopTransitionView : UITransitionView {
230 @implementation PopTransitionView
232 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
233 if (from != nil && to == nil)
234 [self removeFromSuperview];
239 @interface UIView (PopUpView)
240 - (void) popFromSuperviewAnimated:(BOOL)animated;
241 - (void) popSubview:(UIView *)view;
244 @implementation UIView (PopUpView)
246 - (void) popFromSuperviewAnimated:(BOOL)animated {
247 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
250 - (void) popSubview:(UIView *)view {
251 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
252 [transition setDelegate:transition];
253 [self addSubview:transition];
255 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
256 [transition transition:UITransitionNone toView:blank];
257 [transition transition:UITransitionPushFromBottom toView:view];
263 #define lprintf(args...) fprintf(stderr, args)
266 #define ForSaurik (1 && !ForRelease)
267 #define IgnoreInstall (0 && !ForRelease)
268 #define RecycleWebViews 0
269 #define AlwaysReload (1 && !ForRelease)
272 @interface NSMutableArray (Radix)
273 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
276 @implementation NSMutableArray (Radix)
278 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
279 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
280 [invocation setSelector:selector];
281 [invocation setArgument:&object atIndex:2];
283 size_t count([self count]);
288 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
290 for (size_t i(0); i != count; ++i) {
291 RadixItem &item(lhs[i]);
294 id object([self objectAtIndex:i]);
295 [invocation setTarget:object];
298 [invocation getReturnValue:&item.key];
301 static const size_t width = 32;
302 static const size_t bits = 11;
303 static const size_t slots = 1 << bits;
304 static const size_t passes = (width + (bits - 1)) / bits;
306 size_t *hist(new size_t[slots]);
308 for (size_t pass(0); pass != passes; ++pass) {
309 memset(hist, 0, sizeof(size_t) * slots);
311 for (size_t i(0); i != count; ++i) {
312 uint32_t key(lhs[i].key);
314 key &= _not(uint32_t) >> width - bits;
319 for (size_t i(0); i != slots; ++i) {
320 size_t local(offset);
325 for (size_t i(0); i != count; ++i) {
326 uint32_t key(lhs[i].key);
328 key &= _not(uint32_t) >> width - bits;
329 rhs[hist[key]++] = lhs[i];
339 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
340 for (size_t i(0); i != count; ++i)
341 [values addObject:[self objectAtIndex:lhs[i].index]];
342 [self setArray:values];
350 /* Apple Bug Fixes {{{ */
351 @implementation UIWebDocumentView (Cydia)
353 - (void) _setScrollerOffset:(CGPoint)offset {
354 UIScroller *scroller([self _scroller]);
356 CGSize size([scroller contentSize]);
357 CGSize bounds([scroller bounds].size);
360 max.x = size.width - bounds.width;
361 max.y = size.height - bounds.height;
369 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
370 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
372 [scroller setOffset:offset];
379 kUIControlEventMouseDown = 1 << 0,
380 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
381 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
382 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
383 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
384 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
385 } UIControlEventMasks;
387 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
388 size_t length([self length] - state->state);
391 else if (length > count)
393 for (size_t i(0); i != length; ++i)
394 objects[i] = [self item:state->state++];
395 state->itemsPtr = objects;
396 state->mutationsPtr = (unsigned long *) self;
400 @interface NSString (UIKit)
401 - (NSString *) stringByAddingPercentEscapes;
402 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
405 @interface NSString (Cydia)
406 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
407 - (NSComparisonResult) compareByPath:(NSString *)other;
410 @implementation NSString (Cydia)
412 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
413 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
416 - (NSComparisonResult) compareByPath:(NSString *)other {
417 NSString *prefix = [self commonPrefixWithString:other options:0];
418 size_t length = [prefix length];
420 NSRange lrange = NSMakeRange(length, [self length] - length);
421 NSRange rrange = NSMakeRange(length, [other length] - length);
423 lrange = [self rangeOfString:@"/" options:0 range:lrange];
424 rrange = [other rangeOfString:@"/" options:0 range:rrange];
426 NSComparisonResult value;
428 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
429 value = NSOrderedSame;
430 else if (lrange.location == NSNotFound)
431 value = NSOrderedAscending;
432 else if (rrange.location == NSNotFound)
433 value = NSOrderedDescending;
435 value = NSOrderedSame;
437 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
438 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
439 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
440 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
442 NSComparisonResult result = [lpath compare:rpath];
443 return result == NSOrderedSame ? value : result;
448 /* Perl-Compatible RegEx {{{ */
458 Pcre(const char *regex) :
463 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
466 lprintf("%d:%s\n", offset, error);
470 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
471 matches_ = new int[(capture_ + 1) * 3];
479 NSString *operator [](size_t match) {
480 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
483 bool operator ()(NSString *data) {
484 // XXX: length is for characters, not for bytes
485 return operator ()([data UTF8String], [data length]);
488 bool operator ()(const char *data, size_t size) {
490 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
494 /* Mime Addresses {{{ */
495 @interface Address : NSObject {
501 - (NSString *) address;
503 + (Address *) addressWithString:(NSString *)string;
504 - (Address *) initWithString:(NSString *)string;
507 @implementation Address
516 - (NSString *) name {
520 - (NSString *) address {
524 + (Address *) addressWithString:(NSString *)string {
525 return [[[Address alloc] initWithString:string] autorelease];
528 + (NSArray *) _attributeKeys {
529 return [NSArray arrayWithObjects:@"address", @"name", nil];
532 - (NSArray *) attributeKeys {
533 return [[self class] _attributeKeys];
536 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
537 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
540 - (Address *) initWithString:(NSString *)string {
541 if ((self = [super init]) != nil) {
542 const char *data = [string UTF8String];
543 size_t size = [string length];
545 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
547 if (address_r(data, size)) {
548 name_ = [address_r[1] retain];
549 address_ = [address_r[2] retain];
551 name_ = [string retain];
559 /* CoreGraphics Primitives {{{ */
570 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
573 Set(space, red, green, blue, alpha);
578 CGColorRelease(color_);
585 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
587 float color[] = {red, green, blue, alpha};
588 color_ = CGColorCreate(space, color);
591 operator CGColorRef() {
597 extern "C" void UISetColor(CGColorRef color);
599 /* Random Global Variables {{{ */
600 static const int PulseInterval_ = 50000;
601 static const int ButtonBarHeight_ = 48;
602 static const float KeyboardTime_ = 0.3f;
604 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
605 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
606 #define NotifyConfig_ "/etc/notify.conf"
608 static CGColor Blue_;
609 static CGColor Blueish_;
610 static CGColor Black_;
612 static CGColor White_;
613 static CGColor Gray_;
615 static NSString *App_;
616 static NSString *Home_;
617 static BOOL Sounds_Keyboard_;
619 static BOOL Advanced_;
623 static BOOL Ignored_;
625 static UIFont *Font12_;
626 static UIFont *Font12Bold_;
627 static UIFont *Font14_;
628 static UIFont *Font18Bold_;
629 static UIFont *Font22Bold_;
631 static const char *Machine_ = NULL;
632 static const NSString *UniqueID_ = nil;
633 static const NSString *Build_ = nil;
636 CGColorSpaceRef space_;
641 static NSDictionary *SectionMap_;
642 static NSMutableDictionary *Metadata_;
643 static NSMutableDictionary *Indices_;
644 static _transient NSMutableDictionary *Settings_;
645 static _transient NSString *Role_;
646 static _transient NSMutableDictionary *Packages_;
647 static _transient NSMutableDictionary *Sections_;
648 static _transient NSMutableDictionary *Sources_;
649 static bool Changed_;
653 static NSMutableArray *Documents_;
656 NSString *GetLastUpdate() {
657 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
660 return @"Never or Unknown";
662 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
663 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
665 CFRelease(formatter);
667 return [(NSString *) formatted autorelease];
670 /* Display Helpers {{{ */
671 inline float Interpolate(float begin, float end, float fraction) {
672 return (end - begin) * fraction + begin;
675 NSString *SizeString(double size) {
676 bool negative = size < 0;
681 while (size > 1024) {
686 static const char *powers_[] = {"B", "kB", "MB", "GB"};
688 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
691 NSString *StripVersion(NSString *version) {
692 NSRange colon = [version rangeOfString:@":"];
693 if (colon.location != NSNotFound)
694 version = [version substringFromIndex:(colon.location + 1)];
698 NSString *Simplify(NSString *title) {
699 const char *data = [title UTF8String];
700 size_t size = [title length];
702 static Pcre square_r("^\\[(.*)\\]$");
703 if (square_r(data, size))
704 return Simplify(square_r[1]);
706 static Pcre paren_r("^\\((.*)\\)$");
707 if (paren_r(data, size))
708 return Simplify(paren_r[1]);
710 static Pcre title_r("^(.*?) \\(.*\\)$");
711 if (title_r(data, size))
712 return Simplify(title_r[1]);
718 bool isSectionVisible(NSString *section) {
719 NSDictionary *metadata = [Sections_ objectForKey:section];
720 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
721 return hidden == nil || ![hidden boolValue];
724 /* Delegate Prototypes {{{ */
728 @interface NSObject (ProgressDelegate)
731 @implementation NSObject(ProgressDelegate)
733 - (void) _setProgressError:(NSArray *)args {
734 [self performSelector:@selector(setProgressError:forPackage:)
735 withObject:[args objectAtIndex:0]
736 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
742 @protocol ProgressDelegate
743 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
744 - (void) setProgressTitle:(NSString *)title;
745 - (void) setProgressPercent:(float)percent;
746 - (void) startProgress;
747 - (void) addProgressOutput:(NSString *)output;
748 - (bool) isCancelling:(size_t)received;
751 @protocol ConfigurationDelegate
752 - (void) repairWithSelector:(SEL)selector;
753 - (void) setConfigurationData:(NSString *)data;
756 @protocol CydiaDelegate
757 - (void) installPackage:(Package *)package;
758 - (void) removePackage:(Package *)package;
759 - (void) slideUp:(UIActionSheet *)alert;
760 - (void) distUpgrade;
763 - (void) askForSettings;
764 - (UIProgressHUD *) addProgressHUD;
765 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
766 - (RVPage *) pageForPackage:(NSString *)name;
767 - (void) openMailToURL:(NSURL *)url;
768 - (void) clearFirstResponder;
772 /* Status Delegation {{{ */
774 public pkgAcquireStatus
777 _transient NSObject<ProgressDelegate> *delegate_;
785 void setDelegate(id delegate) {
786 delegate_ = delegate;
789 virtual bool MediaChange(std::string media, std::string drive) {
793 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
796 virtual void Fetch(pkgAcquire::ItemDesc &item) {
797 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
798 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
801 virtual void Done(pkgAcquire::ItemDesc &item) {
804 virtual void Fail(pkgAcquire::ItemDesc &item) {
806 item.Owner->Status == pkgAcquire::Item::StatIdle ||
807 item.Owner->Status == pkgAcquire::Item::StatDone
811 std::string &error(item.Owner->ErrorText);
815 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
816 NSArray *fields([description componentsSeparatedByString:@" "]);
817 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
819 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
820 withObject:[NSArray arrayWithObjects:
821 [NSString stringWithUTF8String:error.c_str()],
828 virtual bool Pulse(pkgAcquire *Owner) {
829 bool value = pkgAcquireStatus::Pulse(Owner);
832 double(CurrentBytes + CurrentItems) /
833 double(TotalBytes + TotalItems)
836 [delegate_ setProgressPercent:percent];
837 return [delegate_ isCancelling:CurrentBytes] ? false : value;
840 virtual void Start() {
841 [delegate_ startProgress];
844 virtual void Stop() {
848 /* Progress Delegation {{{ */
853 _transient id<ProgressDelegate> delegate_;
856 virtual void Update() {
857 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
858 [delegate_ setProgressPercent:(Percent / 100)];
867 void setDelegate(id delegate) {
868 delegate_ = delegate;
871 virtual void Done() {
872 [delegate_ setProgressPercent:1];
877 /* Database Interface {{{ */
878 @interface Database : NSObject {
880 pkgDepCache::Policy *policy_;
881 pkgRecords *records_;
882 pkgProblemResolver *resolver_;
883 pkgAcquire *fetcher_;
885 SPtr<pkgPackageManager> manager_;
886 pkgSourceList *list_;
888 NSMutableDictionary *sources_;
889 NSMutableArray *packages_;
891 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
900 + (Database *) sharedInstance;
902 - (void) _readCydia:(NSNumber *)fd;
903 - (void) _readStatus:(NSNumber *)fd;
904 - (void) _readOutput:(NSNumber *)fd;
908 - (Package *) packageWithName:(NSString *)name;
910 - (pkgCacheFile &) cache;
911 - (pkgDepCache::Policy *) policy;
912 - (pkgRecords *) records;
913 - (pkgProblemResolver *) resolver;
914 - (pkgAcquire &) fetcher;
915 - (pkgSourceList &) list;
916 - (NSArray *) packages;
917 - (NSArray *) sources;
926 - (void) updateWithStatus:(Status &)status;
928 - (void) setDelegate:(id)delegate;
929 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
933 /* Source Class {{{ */
934 @interface Source : NSObject {
935 NSString *description_;
940 NSString *distribution_;
944 NSString *defaultIcon_;
946 NSDictionary *record_;
950 - (Source *) initWithMetaIndex:(metaIndex *)index;
952 - (NSComparisonResult) compareByNameAndType:(Source *)source;
954 - (NSDictionary *) record;
958 - (NSString *) distribution;
964 - (NSString *) description;
965 - (NSString *) label;
966 - (NSString *) origin;
967 - (NSString *) version;
969 - (NSString *) defaultIcon;
973 @implementation Source
975 #define _clear(field) \
982 _clear(distribution_)
998 + (NSArray *) _attributeKeys {
999 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1002 - (NSArray *) attributeKeys {
1003 return [[self class] _attributeKeys];
1006 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1007 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1010 - (void) setMetaIndex:(metaIndex *)index {
1013 trusted_ = index->IsTrusted();
1015 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1016 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1017 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1019 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1020 if (dindex != NULL) {
1021 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1023 while (std::getline(release, line)) {
1024 std::string::size_type colon(line.find(':'));
1025 if (colon == std::string::npos)
1028 std::string name(line.substr(0, colon));
1029 std::string value(line.substr(colon + 1));
1030 while (!value.empty() && value[0] == ' ')
1031 value = value.substr(1);
1033 if (name == "Default-Icon")
1034 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1035 else if (name == "Description")
1036 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1037 else if (name == "Label")
1038 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1039 else if (name == "Origin")
1040 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1041 else if (name == "Version")
1042 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1046 record_ = [Sources_ objectForKey:[self key]];
1048 record_ = [record_ retain];
1051 - (Source *) initWithMetaIndex:(metaIndex *)index {
1052 if ((self = [super init]) != nil) {
1053 [self setMetaIndex:index];
1057 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1058 NSDictionary *lhr = [self record];
1059 NSDictionary *rhr = [source record];
1062 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1064 NSString *lhs = [self name];
1065 NSString *rhs = [source name];
1067 if ([lhs length] != 0 && [rhs length] != 0) {
1068 unichar lhc = [lhs characterAtIndex:0];
1069 unichar rhc = [rhs characterAtIndex:0];
1071 if (isalpha(lhc) && !isalpha(rhc))
1072 return NSOrderedAscending;
1073 else if (!isalpha(lhc) && isalpha(rhc))
1074 return NSOrderedDescending;
1077 return [lhs compare:rhs options:LaxCompareOptions_];
1080 - (NSDictionary *) record {
1088 - (NSString *) uri {
1092 - (NSString *) distribution {
1093 return distribution_;
1096 - (NSString *) type {
1100 - (NSString *) key {
1101 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1104 - (NSString *) host {
1105 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1108 - (NSString *) name {
1109 return origin_ == nil ? [self host] : origin_;
1112 - (NSString *) description {
1113 return description_;
1116 - (NSString *) label {
1117 return label_ == nil ? [self host] : label_;
1120 - (NSString *) origin {
1124 - (NSString *) version {
1128 - (NSString *) defaultIcon {
1129 return defaultIcon_;
1134 /* Relationship Class {{{ */
1135 @interface Relationship : NSObject {
1140 - (NSString *) type;
1142 - (NSString *) name;
1146 @implementation Relationship
1154 - (NSString *) type {
1162 - (NSString *) name {
1169 /* Package Class {{{ */
1170 @interface Package : NSObject {
1171 pkgCache::PkgIterator iterator_;
1172 _transient Database *database_;
1173 pkgCache::VerIterator version_;
1174 pkgCache::VerFileIterator file_;
1182 NSString *installed_;
1188 NSString *depiction_;
1189 NSString *homepage_;
1195 NSArray *relationships_;
1198 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1199 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1201 - (pkgCache::PkgIterator) iterator;
1203 - (NSString *) section;
1204 - (NSString *) simpleSection;
1208 - (Address *) maintainer;
1210 - (NSString *) description;
1211 - (NSString *) index;
1213 - (NSMutableDictionary *) metadata;
1215 - (BOOL) subscribed;
1218 - (NSString *) latest;
1219 - (NSString *) installed;
1222 - (BOOL) upgradableAndEssential:(BOOL)essential;
1225 - (BOOL) unfiltered;
1229 - (BOOL) halfConfigured;
1230 - (BOOL) halfInstalled;
1232 - (NSString *) mode;
1235 - (NSString *) name;
1236 - (NSString *) tagline;
1238 - (NSString *) homepage;
1239 - (NSString *) depiction;
1240 - (Address *) author;
1242 - (NSArray *) files;
1243 - (NSArray *) relationships;
1244 - (NSArray *) warnings;
1245 - (NSArray *) applications;
1247 - (Source *) source;
1248 - (NSString *) role;
1249 - (NSString *) rating;
1251 - (BOOL) matches:(NSString *)text;
1253 - (bool) hasSupportingRole;
1254 - (BOOL) hasTag:(NSString *)tag;
1255 - (NSString *) primaryPurpose;
1256 - (NSArray *) purposes;
1258 - (NSComparisonResult) compareByName:(Package *)package;
1259 - (NSComparisonResult) compareBySection:(Package *)package;
1261 - (uint32_t) compareForChanges;
1266 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1267 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1268 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1269 - (NSNumber *) isVisibleInSource:(Source *)source;
1273 @implementation Package
1279 if (section_ != nil)
1283 if (installed_ != nil)
1284 [installed_ release];
1292 if (depiction_ != nil)
1293 [depiction_ release];
1294 if (homepage_ != nil)
1295 [homepage_ release];
1296 if (sponsor_ != nil)
1305 if (relationships_ != nil)
1306 [relationships_ release];
1311 + (NSArray *) _attributeKeys {
1312 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1315 - (NSArray *) attributeKeys {
1316 return [[self class] _attributeKeys];
1319 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1320 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1323 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1324 if ((self = [super init]) != nil) {
1325 iterator_ = iterator;
1326 database_ = database;
1328 version_ = [database_ policy]->GetCandidateVer(iterator_);
1329 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1330 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1332 pkgCache::VerIterator current = iterator_.CurrentVer();
1333 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1334 installed_ = [StripVersion(installed) retain];
1336 if (!version_.end())
1337 file_ = version_.FileList();
1339 pkgCache &cache([database_ cache]);
1340 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1343 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1346 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1348 const char *begin, *end;
1349 parser->GetRec(begin, end);
1351 NSString *website(nil);
1352 NSString *sponsor(nil);
1353 NSString *author(nil);
1362 {"depiction", &depiction_},
1363 {"homepage", &homepage_},
1364 {"website", &website},
1365 {"sponsor", &sponsor},
1366 {"author", &author},
1370 while (begin != end)
1371 if (*begin == '\n') {
1374 } else if (isblank(*begin)) next: {
1375 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1378 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1379 const char *name(begin);
1380 size_t size(colon - begin);
1382 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1385 const char *stop(begin == NULL ? end : begin);
1386 while (stop[-1] == '\r')
1388 while (++colon != stop && isblank(*colon));
1390 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1391 if (strncasecmp(names[i].name_, name, size) == 0) {
1392 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1393 *names[i].value_ = value;
1404 name_ = [name_ retain];
1405 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1407 icon_ = [icon_ retain];
1408 if (depiction_ != nil)
1409 depiction_ = [depiction_ retain];
1410 if (homepage_ == nil)
1411 homepage_ = website;
1412 if ([homepage_ isEqualToString:depiction_])
1414 if (homepage_ != nil)
1415 homepage_ = [homepage_ retain];
1417 sponsor_ = [[Address addressWithString:sponsor] retain];
1419 author_ = [[Address addressWithString:author] retain];
1421 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1425 for (int i(0), e([tags_ count]); i != e; ++i) {
1426 NSString *tag = [tags_ objectAtIndex:i];
1427 if ([tag hasPrefix:@"role::"]) {
1428 role_ = [[tag substringFromIndex:6] retain];
1433 NSString *solid(latest == nil ? installed : latest);
1434 bool changed(false);
1436 NSString *key([id_ lowercaseString]);
1438 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1439 if (metadata == nil) {
1440 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1445 [metadata setObject:solid forKey:@"LastVersion"];
1448 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1449 NSDate *last([metadata objectForKey:@"LastSeen"]);
1450 NSString *version([metadata objectForKey:@"LastVersion"]);
1453 first = last == nil ? now_ : last;
1454 [metadata setObject:first forKey:@"FirstSeen"];
1459 if (version == nil) {
1460 [metadata setObject:solid forKey:@"LastVersion"];
1462 } else if (![version isEqualToString:solid]) {
1463 [metadata setObject:solid forKey:@"LastVersion"];
1465 [metadata setObject:last forKey:@"LastSeen"];
1471 [Packages_ setObject:metadata forKey:key];
1477 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1478 return [[[Package alloc]
1479 initWithIterator:iterator
1484 - (pkgCache::PkgIterator) iterator {
1488 - (NSString *) section {
1489 if (section_ != nil)
1492 const char *section = iterator_.Section();
1493 if (section == NULL)
1496 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1499 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1500 if (NSString *rename = [value objectForKey:@"Rename"]) {
1505 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1509 - (NSString *) simpleSection {
1510 if (NSString *section = [self section])
1511 return Simplify(section);
1516 - (NSString *) uri {
1519 pkgIndexFile *index;
1520 pkgCache::PkgFileIterator file(file_.File());
1521 if (![database_ list].FindIndex(file, index))
1523 return [NSString stringWithUTF8String:iterator_->Path];
1524 //return [NSString stringWithUTF8String:file.Site()];
1525 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
1529 - (Address *) maintainer {
1532 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1533 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1537 return version_.end() ? 0 : version_->InstalledSize;
1540 - (NSString *) description {
1543 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1544 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1546 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1547 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1548 if ([lines count] < 2)
1551 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1552 for (size_t i(1); i != [lines count]; ++i) {
1553 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1554 [trimmed addObject:trim];
1557 return [trimmed componentsJoinedByString:@"\n"];
1560 - (NSString *) index {
1561 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1562 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1565 - (NSMutableDictionary *) metadata {
1566 return [Packages_ objectForKey:[id_ lowercaseString]];
1570 NSDictionary *metadata([self metadata]);
1571 if ([self subscribed])
1572 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1574 return [metadata objectForKey:@"FirstSeen"];
1577 - (BOOL) subscribed {
1578 NSDictionary *metadata([self metadata]);
1579 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1580 return [subscribed boolValue];
1586 NSDictionary *metadata([self metadata]);
1587 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1588 return [ignored boolValue];
1593 - (NSString *) latest {
1597 - (NSString *) installed {
1602 return !version_.end();
1605 - (BOOL) upgradableAndEssential:(BOOL)essential {
1606 pkgCache::VerIterator current = iterator_.CurrentVer();
1610 value = essential && [self essential];
1612 value = !version_.end() && version_ != current && (!essential || ![database_ cache][iterator_].Keep());
1616 - (BOOL) essential {
1617 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1621 return [database_ cache][iterator_].InstBroken();
1624 - (BOOL) unfiltered {
1625 NSString *section = [self section];
1626 return section == nil || isSectionVisible(section);
1630 return [self hasSupportingRole] && [self unfiltered];
1634 unsigned char current = iterator_->CurrentState;
1635 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1638 - (BOOL) halfConfigured {
1639 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1642 - (BOOL) halfInstalled {
1643 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1647 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1648 return state.Mode != pkgDepCache::ModeKeep;
1651 - (NSString *) mode {
1652 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1654 switch (state.Mode) {
1655 case pkgDepCache::ModeDelete:
1656 if ((state.iFlags & pkgDepCache::Purge) != 0)
1660 case pkgDepCache::ModeKeep:
1661 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1665 case pkgDepCache::ModeInstall:
1666 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1667 return @"Reinstall";
1668 else switch (state.Status) {
1670 return @"Downgrade";
1676 return @"New Install";
1689 - (NSString *) name {
1690 return name_ == nil ? id_ : name_;
1693 - (NSString *) tagline {
1697 - (UIImage *) icon {
1698 NSString *section = [self simpleSection];
1701 if (NSString *icon = icon_)
1702 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1703 if (icon == nil) if (section != nil)
1704 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1705 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1706 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1708 icon = [UIImage applicationImageNamed:@"unknown.png"];
1712 - (NSString *) homepage {
1716 - (NSString *) depiction {
1720 - (Address *) sponsor {
1724 - (Address *) author {
1728 - (NSArray *) files {
1729 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1730 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1733 fin.open([path UTF8String]);
1738 while (std::getline(fin, line))
1739 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1744 - (NSArray *) relationships {
1745 return relationships_;
1748 - (NSArray *) warnings {
1749 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1750 const char *name(iterator_.Name());
1752 size_t length(strlen(name));
1753 if (length < 2) invalid:
1754 [warnings addObject:@"illegal package identifier"];
1755 else for (size_t i(0); i != length; ++i)
1757 /* XXX: technically this is not allowed */
1758 (name[i] < 'A' || name[i] > 'Z') &&
1759 (name[i] < 'a' || name[i] > 'z') &&
1760 (name[i] < '0' || name[i] > '9') &&
1761 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1764 if (strcmp(name, "cydia") != 0) {
1766 bool _private = false;
1769 bool repository = [[self section] isEqualToString:@"Repositories"];
1771 if (NSArray *files = [self files])
1772 for (NSString *file in files)
1773 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1775 else if (!_private && [file isEqualToString:@"/private"])
1777 else if (!stash && [file isEqualToString:@"/var/stash"])
1780 /* XXX: this is not sensitive enough. only some folders are valid. */
1781 if (cydia && !repository)
1782 [warnings addObject:@"files installed into Cydia.app"];
1784 [warnings addObject:@"files installed with /private/*"];
1786 [warnings addObject:@"files installed to /var/stash"];
1789 return [warnings count] == 0 ? nil : warnings;
1792 - (NSArray *) applications {
1793 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1795 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1797 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1798 if (NSArray *files = [self files])
1799 for (NSString *file in files)
1800 if (application_r(file)) {
1801 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1802 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1803 if ([id isEqualToString:me])
1806 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1808 display = application_r[1];
1810 NSString *bundle([file stringByDeletingLastPathComponent]);
1811 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1812 if (icon == nil || [icon length] == 0)
1814 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1816 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1817 [applications addObject:application];
1819 [application addObject:id];
1820 [application addObject:display];
1821 [application addObject:url];
1824 return [applications count] == 0 ? nil : applications;
1827 - (Source *) source {
1829 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1836 - (NSString *) role {
1840 - (NSString *) rating {
1841 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1842 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1847 - (BOOL) matches:(NSString *)text {
1853 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1854 if (range.location != NSNotFound)
1857 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1858 if (range.location != NSNotFound)
1861 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1862 if (range.location != NSNotFound)
1868 - (bool) hasSupportingRole {
1871 if ([role_ isEqualToString:@"enduser"])
1873 if ([Role_ isEqualToString:@"User"])
1875 if ([role_ isEqualToString:@"hacker"])
1877 if ([Role_ isEqualToString:@"Hacker"])
1879 if ([role_ isEqualToString:@"developer"])
1881 if ([Role_ isEqualToString:@"Developer"])
1886 - (BOOL) hasTag:(NSString *)tag {
1887 return tags_ == nil ? NO : [tags_ containsObject:tag];
1890 - (NSString *) primaryPurpose {
1891 for (NSString *tag in tags_)
1892 if ([tag hasPrefix:@"purpose::"])
1893 return [tag substringFromIndex:9];
1897 - (NSArray *) purposes {
1898 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1899 for (NSString *tag in tags_)
1900 if ([tag hasPrefix:@"purpose::"])
1901 [purposes addObject:[tag substringFromIndex:9]];
1902 return [purposes count] == 0 ? nil : purposes;
1905 - (NSComparisonResult) compareByName:(Package *)package {
1906 NSString *lhs = [self name];
1907 NSString *rhs = [package name];
1909 if ([lhs length] != 0 && [rhs length] != 0) {
1910 unichar lhc = [lhs characterAtIndex:0];
1911 unichar rhc = [rhs characterAtIndex:0];
1913 if (isalpha(lhc) && !isalpha(rhc))
1914 return NSOrderedAscending;
1915 else if (!isalpha(lhc) && isalpha(rhc))
1916 return NSOrderedDescending;
1919 return [lhs compare:rhs options:LaxCompareOptions_];
1922 - (NSComparisonResult) compareBySection:(Package *)package {
1923 NSString *lhs = [self section];
1924 NSString *rhs = [package section];
1926 if (lhs == NULL && rhs != NULL)
1927 return NSOrderedAscending;
1928 else if (lhs != NULL && rhs == NULL)
1929 return NSOrderedDescending;
1930 else if (lhs != NULL && rhs != NULL) {
1931 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1932 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1935 return NSOrderedSame;
1938 - (uint32_t) compareForChanges {
1943 uint32_t timestamp : 30;
1944 uint32_t ignored : 1;
1945 uint32_t upgradable : 1;
1949 bool upgradable([self upgradableAndEssential:YES]);
1950 value.bits.upgradable = upgradable ? 1 : 0;
1953 value.bits.timestamp = 0;
1954 value.bits.ignored = [self ignored] ? 0 : 1;
1955 value.bits.upgradable = 1;
1957 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1958 value.bits.ignored = 0;
1959 value.bits.upgradable = 0;
1962 return _not(uint32_t) - value.key;
1966 pkgProblemResolver *resolver = [database_ resolver];
1967 resolver->Clear(iterator_);
1968 resolver->Protect(iterator_);
1969 pkgCacheFile &cache([database_ cache]);
1970 cache->MarkInstall(iterator_, false);
1971 pkgDepCache::StateCache &state((*cache)[iterator_]);
1972 if (!state.Install())
1973 cache->SetReInstall(iterator_, true);
1977 pkgProblemResolver *resolver = [database_ resolver];
1978 resolver->Clear(iterator_);
1979 resolver->Protect(iterator_);
1980 resolver->Remove(iterator_);
1981 [database_ cache]->MarkDelete(iterator_, true);
1984 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1985 return [NSNumber numberWithBool:(
1986 [self unfiltered] && [self matches:search]
1990 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1991 return [NSNumber numberWithBool:(
1992 (![number boolValue] || [self visible]) && [self installed] != nil
1996 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1997 NSString *section = [self section];
1999 return [NSNumber numberWithBool:(
2001 [self installed] == nil && (
2003 section == nil && [name length] == 0 ||
2004 [name isEqualToString:section]
2009 - (NSNumber *) isVisibleInSource:(Source *)source {
2010 return [NSNumber numberWithBool:([self source] == source && [self visible])];
2015 /* Section Class {{{ */
2016 @interface Section : NSObject {
2022 - (NSComparisonResult) compareByName:(Section *)section;
2023 - (Section *) initWithName:(NSString *)name;
2024 - (Section *) initWithName:(NSString *)name row:(size_t)row;
2025 - (NSString *) name;
2028 - (void) addToCount;
2032 @implementation Section
2039 - (NSComparisonResult) compareByName:(Section *)section {
2040 NSString *lhs = [self name];
2041 NSString *rhs = [section name];
2043 if ([lhs length] != 0 && [rhs length] != 0) {
2044 unichar lhc = [lhs characterAtIndex:0];
2045 unichar rhc = [rhs characterAtIndex:0];
2047 if (isalpha(lhc) && !isalpha(rhc))
2048 return NSOrderedAscending;
2049 else if (!isalpha(lhc) && isalpha(rhc))
2050 return NSOrderedDescending;
2053 return [lhs compare:rhs options:LaxCompareOptions_];
2056 - (Section *) initWithName:(NSString *)name {
2057 return [self initWithName:name row:0];
2060 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2061 if ((self = [super init]) != nil) {
2062 name_ = [name retain];
2067 - (NSString *) name {
2079 - (void) addToCount {
2087 static NSArray *Finishes_;
2089 /* Database Implementation {{{ */
2090 @implementation Database
2092 + (Database *) sharedInstance {
2093 static Database *instance;
2094 if (instance == nil)
2095 instance = [[Database alloc] init];
2104 - (void) _readCydia:(NSNumber *)fd { _pooled
2105 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2106 std::istream is(&ib);
2109 static Pcre finish_r("^finish:([^:]*)$");
2111 while (std::getline(is, line)) {
2112 const char *data(line.c_str());
2113 size_t size = line.size();
2114 lprintf("C:%s\n", data);
2116 if (finish_r(data, size)) {
2117 NSString *finish = finish_r[1];
2118 int index = [Finishes_ indexOfObject:finish];
2119 if (index != INT_MAX && index > Finish_)
2127 - (void) _readStatus:(NSNumber *)fd { _pooled
2128 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2129 std::istream is(&ib);
2132 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2133 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2135 while (std::getline(is, line)) {
2136 const char *data(line.c_str());
2137 size_t size = line.size();
2138 lprintf("S:%s\n", data);
2140 if (conffile_r(data, size)) {
2141 [delegate_ setConfigurationData:conffile_r[1]];
2142 } else if (strncmp(data, "status: ", 8) == 0) {
2143 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2144 [delegate_ setProgressTitle:string];
2145 } else if (pmstatus_r(data, size)) {
2146 std::string type([pmstatus_r[1] UTF8String]);
2147 NSString *id = pmstatus_r[2];
2149 float percent([pmstatus_r[3] floatValue]);
2150 [delegate_ setProgressPercent:(percent / 100)];
2152 NSString *string = pmstatus_r[4];
2154 if (type == "pmerror")
2155 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2156 withObject:[NSArray arrayWithObjects:string, id, nil]
2159 else if (type == "pmstatus") {
2160 [delegate_ setProgressTitle:string];
2161 } else if (type == "pmconffile")
2162 [delegate_ setConfigurationData:string];
2163 else _assert(false);
2164 } else _assert(false);
2170 - (void) _readOutput:(NSNumber *)fd { _pooled
2171 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2172 std::istream is(&ib);
2175 while (std::getline(is, line)) {
2176 lprintf("O:%s\n", line.c_str());
2177 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2187 - (Package *) packageWithName:(NSString *)name {
2188 if (static_cast<pkgDepCache *>(cache_) == NULL)
2190 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2191 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2194 - (Database *) init {
2195 if ((self = [super init]) != nil) {
2202 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2203 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2207 _assert(pipe(fds) != -1);
2210 _config->Set("APT::Keep-Fds::", cydiafd_);
2211 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2214 detachNewThreadSelector:@selector(_readCydia:)
2216 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2219 _assert(pipe(fds) != -1);
2223 detachNewThreadSelector:@selector(_readStatus:)
2225 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2228 _assert(pipe(fds) != -1);
2229 _assert(dup2(fds[0], 0) != -1);
2230 _assert(close(fds[0]) != -1);
2232 input_ = fdopen(fds[1], "a");
2234 _assert(pipe(fds) != -1);
2235 _assert(dup2(fds[1], 1) != -1);
2236 _assert(close(fds[1]) != -1);
2239 detachNewThreadSelector:@selector(_readOutput:)
2241 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2246 - (pkgCacheFile &) cache {
2250 - (pkgDepCache::Policy *) policy {
2254 - (pkgRecords *) records {
2258 - (pkgProblemResolver *) resolver {
2262 - (pkgAcquire &) fetcher {
2266 - (pkgSourceList &) list {
2270 - (NSArray *) packages {
2274 - (NSArray *) sources {
2275 return [sources_ allValues];
2278 - (NSArray *) issues {
2279 if (cache_->BrokenCount() == 0)
2282 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2284 for (Package *package in packages_) {
2285 if (![package broken])
2287 pkgCache::PkgIterator pkg([package iterator]);
2289 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2290 [entry addObject:[package name]];
2291 [issues addObject:entry];
2293 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2297 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2298 pkgCache::DepIterator start;
2299 pkgCache::DepIterator end;
2300 dep.GlobOr(start, end); // ++dep
2302 if (!cache_->IsImportantDep(end))
2304 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2307 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2308 [entry addObject:failure];
2309 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2311 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2312 [failure addObject:[package name]];
2314 pkgCache::PkgIterator target(start.TargetPkg());
2315 if (target->ProvidesList != 0)
2316 [failure addObject:@"?"];
2318 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2320 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2321 else if (!cache_[target].CandidateVerIter(cache_).end())
2322 [failure addObject:@"-"];
2323 else if (target->ProvidesList == 0)
2324 [failure addObject:@"!"];
2326 [failure addObject:@"%"];
2330 if (start.TargetVer() != 0)
2331 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2342 - (void) reloadData {
2362 if (!cache_.Open(progress_, true)) {
2364 if (!_error->PopMessage(error))
2367 lprintf("cache_.Open():[%s]\n", error.c_str());
2369 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2370 [delegate_ repairWithSelector:@selector(configure)];
2371 else if (error == "The package lists or status file could not be parsed or opened.")
2372 [delegate_ repairWithSelector:@selector(update)];
2373 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2374 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2375 // else if (error == "The list of sources could not be read.")
2376 else _assert(false);
2382 now_ = [[NSDate date] retain];
2384 policy_ = new pkgDepCache::Policy();
2385 records_ = new pkgRecords(cache_);
2386 resolver_ = new pkgProblemResolver(cache_);
2387 fetcher_ = new pkgAcquire(&status_);
2390 list_ = new pkgSourceList();
2391 _assert(list_->ReadMainList());
2393 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2394 _assert(pkgApplyStatus(cache_));
2396 if (cache_->BrokenCount() != 0) {
2397 _assert(pkgFixBroken(cache_));
2398 _assert(cache_->BrokenCount() == 0);
2399 _assert(pkgMinimizeUpgrade(cache_));
2402 [sources_ removeAllObjects];
2403 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2404 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2405 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2407 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2408 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2412 [packages_ removeAllObjects];
2415 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2416 if (Package *package = [Package packageWithIterator:iterator database:self])
2417 [packages_ addObject:package];
2419 [packages_ sortUsingSelector:@selector(compareByName:)];
2423 - (void) configure {
2424 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2425 system([dpkg UTF8String]);
2433 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2434 _assert(!_error->PendingError());
2437 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2440 public pkgArchiveCleaner
2443 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2448 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2450 while (_error->PopMessage(error))
2451 lprintf("ArchiveCleaner: %s\n", error.c_str());
2456 pkgRecords records(cache_);
2458 lock_ = new FileFd();
2459 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2460 _assert(!_error->PendingError());
2463 // XXX: explain this with an error message
2464 _assert(list.ReadMainList());
2466 manager_ = (_system->CreatePM(cache_));
2467 _assert(manager_->GetArchives(fetcher_, &list, &records));
2468 _assert(!_error->PendingError());
2472 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2474 _assert(list.ReadMainList());
2475 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2476 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2479 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2484 bool failed = false;
2485 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2486 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2489 std::string uri = (*item)->DescURI();
2490 std::string error = (*item)->ErrorText;
2492 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2495 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2496 withObject:[NSArray arrayWithObjects:
2497 [NSString stringWithUTF8String:error.c_str()],
2509 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2511 if (_error->PendingError()) {
2516 if (result == pkgPackageManager::Failed) {
2521 if (result != pkgPackageManager::Completed) {
2526 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2528 _assert(list.ReadMainList());
2529 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2530 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2533 if (![before isEqualToArray:after])
2538 _assert(pkgDistUpgrade(cache_));
2542 [self updateWithStatus:status_];
2545 - (void) updateWithStatus:(Status &)status {
2547 _assert(list.ReadMainList());
2550 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2551 _assert(!_error->PendingError());
2553 pkgAcquire fetcher(&status);
2554 _assert(list.GetIndexes(&fetcher));
2556 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2557 bool failed = false;
2558 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2559 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2560 (*item)->Finished();
2564 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2565 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2566 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2569 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2574 - (void) setDelegate:(id)delegate {
2575 delegate_ = delegate;
2576 status_.setDelegate(delegate);
2577 progress_.setDelegate(delegate);
2580 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2581 pkgIndexFile *index(NULL);
2582 list_->FindIndex(file, index);
2583 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2589 /* PopUp Windows {{{ */
2590 @interface PopUpView : UIView {
2591 _transient id delegate_;
2592 UITransitionView *transition_;
2597 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2601 @implementation PopUpView
2604 [transition_ setDelegate:nil];
2605 [transition_ release];
2611 [transition_ transition:UITransitionPushFromTop toView:nil];
2614 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2615 if (from != nil && to == nil)
2616 [self removeFromSuperview];
2619 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2620 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2621 delegate_ = delegate;
2623 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2624 [self addSubview:transition_];
2626 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2628 [view addSubview:self];
2630 [transition_ setDelegate:self];
2632 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2633 [transition_ transition:UITransitionNone toView:blank];
2634 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2641 /* Mail Composition {{{ */
2642 @interface MailToView : PopUpView {
2643 MailComposeController *controller_;
2646 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2650 @implementation MailToView
2653 [controller_ release];
2657 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2661 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2662 NSLog(@"did:%@", delivery);
2663 // [UIApp setStatusBarShowsProgress:NO];
2664 if ([controller error]){
2665 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2666 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2667 [mailAlertSheet setBodyText:[controller error]];
2668 [mailAlertSheet popupAlertAnimated:YES];
2672 - (void) showError {
2673 NSLog(@"%@", [controller_ error]);
2674 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2675 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2676 [mailAlertSheet setBodyText:[controller_ error]];
2677 [mailAlertSheet popupAlertAnimated:YES];
2680 - (void) deliverMessage { _pooled
2684 if (![controller_ deliverMessage])
2685 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2688 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2689 if ([controller_ needsDelivery])
2690 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2695 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2696 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2697 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2698 [controller_ setDelegate:self];
2699 [controller_ initializeUI];
2700 [controller_ setupForURL:url];
2702 UIView *view([controller_ view]);
2703 [overlay_ addSubview:view];
2709 /* Confirmation View {{{ */
2710 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2711 if (!iterator.end())
2712 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2713 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2715 pkgCache::PkgIterator package(dep.TargetPkg());
2718 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2725 @protocol ConfirmationViewDelegate
2730 @interface ConfirmationView : BrowserView {
2731 _transient Database *database_;
2732 UIActionSheet *essential_;
2739 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2743 @implementation ConfirmationView
2750 if (essential_ != nil)
2751 [essential_ release];
2757 [book_ popFromSuperviewAnimated:YES];
2760 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2761 NSString *context([sheet context]);
2763 if ([context isEqualToString:@"remove"]) {
2771 [delegate_ confirm];
2778 } else if ([context isEqualToString:@"unable"]) {
2782 [super alertSheet:sheet buttonClicked:button];
2785 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2786 [window setValue:changes_ forKey:@"changes"];
2787 [window setValue:issues_ forKey:@"issues"];
2788 [window setValue:sizes_ forKey:@"sizes"];
2789 [super webView:sender didClearWindowObject:window forFrame:frame];
2792 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2793 if ((self = [super initWithBook:book]) != nil) {
2794 database_ = database;
2796 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2797 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2798 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2799 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2800 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2804 pkgDepCache::Policy *policy([database_ policy]);
2806 pkgCacheFile &cache([database_ cache]);
2807 NSArray *packages = [database_ packages];
2808 for (size_t i(0), e = [packages count]; i != e; ++i) {
2809 Package *package = [packages objectAtIndex:i];
2810 pkgCache::PkgIterator iterator = [package iterator];
2811 pkgDepCache::StateCache &state(cache[iterator]);
2813 NSString *name([package name]);
2815 if (state.NewInstall())
2816 [installing addObject:name];
2817 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2818 [reinstalling addObject:name];
2819 else if (state.Upgrade())
2820 [upgrading addObject:name];
2821 else if (state.Downgrade())
2822 [downgrading addObject:name];
2823 else if (state.Delete()) {
2824 if ([package essential])
2826 [removing addObject:name];
2829 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2830 substrate_ |= DepSubstrate(iterator.CurrentVer());
2835 else if (Advanced_ || true) {
2836 essential_ = [[UIActionSheet alloc]
2837 initWithTitle:@"Removing Essentials"
2838 buttons:[NSArray arrayWithObjects:
2839 @"Cancel Operation (Safe)",
2840 @"Force Removal (Unsafe)",
2842 defaultButtonIndex:0
2848 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2850 [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."];
2852 essential_ = [[UIActionSheet alloc]
2853 initWithTitle:@"Unable to Comply"
2854 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2855 defaultButtonIndex:0
2860 [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."];
2863 changes_ = [[NSArray alloc] initWithObjects:
2871 issues_ = [database_ issues];
2873 issues_ = [issues_ retain];
2875 sizes_ = [[NSArray alloc] initWithObjects:
2876 SizeString([database_ fetcher].FetchNeeded()),
2877 SizeString([database_ fetcher].PartialPresent()),
2878 SizeString([database_ cache]->UsrSize()),
2881 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2885 - (NSString *) backButtonTitle {
2889 - (NSString *) leftButtonTitle {
2893 - (id) _rightButtonTitle {
2894 #if AlwaysReload || IgnoreInstall
2897 return issues_ == nil ? @"Confirm" : nil;
2901 - (void) _leftButtonClicked {
2906 - (void) _rightButtonClicked {
2908 return [super _rightButtonClicked];
2910 if (essential_ != nil)
2911 [essential_ popupAlertAnimated:YES];
2915 [delegate_ confirm];
2923 /* Progress Data {{{ */
2924 @interface ProgressData : NSObject {
2930 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2937 @implementation ProgressData
2939 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2940 if ((self = [super init]) != nil) {
2941 selector_ = selector;
2961 /* Progress View {{{ */
2962 @interface ProgressView : UIView <
2963 ConfigurationDelegate,
2966 _transient Database *database_;
2968 UIView *background_;
2969 UITransitionView *transition_;
2971 UINavigationBar *navbar_;
2972 UIProgressBar *progress_;
2973 UITextView *output_;
2974 UITextLabel *status_;
2975 UIPushButton *close_;
2978 SHA1SumValue springlist_;
2979 SHA1SumValue notifyconf_;
2980 SHA1SumValue sandplate_;
2982 NSTimeInterval last_;
2985 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2987 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2988 - (void) setContentView:(UIView *)view;
2991 - (void) _retachThread;
2992 - (void) _detachNewThreadData:(ProgressData *)data;
2993 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2999 @protocol ProgressViewDelegate
3000 - (void) progressViewIsComplete:(ProgressView *)sender;
3003 @implementation ProgressView
3006 [transition_ setDelegate:nil];
3007 [navbar_ setDelegate:nil];
3010 if (background_ != nil)
3011 [background_ release];
3012 [transition_ release];
3015 [progress_ release];
3022 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
3023 if (bootstrap_ && from == overlay_ && to == view_)
3027 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
3028 if ((self = [super initWithFrame:frame]) != nil) {
3029 database_ = database;
3030 delegate_ = delegate;
3032 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3033 [transition_ setDelegate:self];
3035 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3038 [overlay_ setBackgroundColor:[UIColor blackColor]];
3040 background_ = [[UIView alloc] initWithFrame:[self bounds]];
3041 [background_ setBackgroundColor:[UIColor blackColor]];
3042 [self addSubview:background_];
3045 [self addSubview:transition_];
3047 CGSize navsize = [UINavigationBar defaultSize];
3048 CGRect navrect = {{0, 0}, navsize};
3050 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3051 [overlay_ addSubview:navbar_];
3053 [navbar_ setBarStyle:1];
3054 [navbar_ setDelegate:self];
3056 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3057 [navbar_ pushNavigationItem:navitem];
3059 CGRect bounds = [overlay_ bounds];
3060 CGSize prgsize = [UIProgressBar defaultSize];
3063 (bounds.size.width - prgsize.width) / 2,
3064 bounds.size.height - prgsize.height - 20
3067 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3068 [progress_ setStyle:0];
3070 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3072 bounds.size.height - prgsize.height - 50,
3073 bounds.size.width - 20,
3077 [status_ setColor:[UIColor whiteColor]];
3078 [status_ setBackgroundColor:[UIColor clearColor]];
3080 [status_ setCentersHorizontally:YES];
3081 //[status_ setFont:font];
3083 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3085 navrect.size.height + 20,
3086 bounds.size.width - 20,
3087 bounds.size.height - navsize.height - 62 - navrect.size.height
3090 //[output_ setTextFont:@"Courier New"];
3091 [output_ setTextSize:12];
3093 [output_ setTextColor:[UIColor whiteColor]];
3094 [output_ setBackgroundColor:[UIColor clearColor]];
3096 [output_ setMarginTop:0];
3097 [output_ setAllowsRubberBanding:YES];
3098 [output_ setEditable:NO];
3100 [overlay_ addSubview:output_];
3102 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3104 bounds.size.height - prgsize.height - 50,
3105 bounds.size.width - 20,
3109 [close_ setAutosizesToFit:NO];
3110 [close_ setDrawsShadow:YES];
3111 [close_ setStretchBackground:YES];
3112 [close_ setEnabled:YES];
3114 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3115 [close_ setTitleFont:bold];
3117 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3118 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3119 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3123 - (void) setContentView:(UIView *)view {
3124 view_ = [view retain];
3127 - (void) resetView {
3128 [transition_ transition:6 toView:view_];
3131 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3132 NSString *context([sheet context]);
3134 if ([context isEqualToString:@"error"])
3136 else if ([context isEqualToString:@"conffile"]) {
3137 FILE *input = [database_ input];
3141 fprintf(input, "N\n");
3145 fprintf(input, "Y\n");
3156 - (void) closeButtonPushed {
3165 [delegate_ suspendWithAnimation:YES];
3169 system("launchctl stop com.apple.SpringBoard");
3173 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3182 - (void) _retachThread {
3183 UINavigationItem *item = [navbar_ topItem];
3184 [item setTitle:@"Complete"];
3186 [overlay_ addSubview:close_];
3187 [progress_ removeFromSuperview];
3188 [status_ removeFromSuperview];
3190 [delegate_ progressViewIsComplete:self];
3193 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3194 MMap mmap(file, MMap::ReadOnly);
3196 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3197 if (!(sandplate_ == sha1.Result()))
3202 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3203 MMap mmap(file, MMap::ReadOnly);
3205 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3206 if (!(notifyconf_ == sha1.Result()))
3211 FileFd file(SpringBoard_, FileFd::ReadOnly);
3212 MMap mmap(file, MMap::ReadOnly);
3214 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3215 if (!(springlist_ == sha1.Result()))
3220 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3221 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3222 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3223 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3224 case 4: [close_ setTitle:@"Reboot Device"]; break;
3227 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3229 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3230 [cache autorelease];
3232 NSFileManager *manager = [NSFileManager defaultManager];
3233 NSError *error = nil;
3235 id system = [cache objectForKey:@"System"];
3240 if (stat(Cache_, &info) == -1)
3243 [system removeAllObjects];
3245 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3246 for (NSString *app in apps)
3247 if ([app hasSuffix:@".app"]) {
3248 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3249 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3250 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3252 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3253 [info setObject:path forKey:@"Path"];
3254 [info setObject:@"System" forKey:@"ApplicationType"];
3255 [system addInfoDictionary:info];
3261 [cache writeToFile:@Cache_ atomically:YES];
3263 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3265 if (chmod(Cache_, info.st_mode) == -1)
3269 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3272 notify_post("com.apple.mobile.application_installed");
3274 [delegate_ setStatusBarShowsProgress:NO];
3277 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3278 [[data target] performSelector:[data selector] withObject:[data object]];
3281 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3284 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3285 UINavigationItem *item = [navbar_ topItem];
3286 [item setTitle:title];
3288 [status_ setText:nil];
3289 [output_ setText:@""];
3290 [progress_ setProgress:0];
3293 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3295 [close_ removeFromSuperview];
3296 [overlay_ addSubview:progress_];
3297 [overlay_ addSubview:status_];
3299 [delegate_ setStatusBarShowsProgress:YES];
3303 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3304 MMap mmap(file, MMap::ReadOnly);
3306 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3307 sandplate_ = sha1.Result();
3311 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3312 MMap mmap(file, MMap::ReadOnly);
3314 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3315 notifyconf_ = sha1.Result();
3319 FileFd file(SpringBoard_, FileFd::ReadOnly);
3320 MMap mmap(file, MMap::ReadOnly);
3322 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3323 springlist_ = sha1.Result();
3326 [transition_ transition:6 toView:overlay_];
3329 detachNewThreadSelector:@selector(_detachNewThreadData:)
3331 withObject:[[ProgressData alloc]
3332 initWithSelector:selector
3339 - (void) repairWithSelector:(SEL)selector {
3341 detachNewThreadSelector:selector
3348 - (void) setConfigurationData:(NSString *)data {
3350 performSelectorOnMainThread:@selector(_setConfigurationData:)
3356 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3357 Package *package = id == nil ? nil : [database_ packageWithName:id];
3359 UIActionSheet *sheet = [[[UIActionSheet alloc]
3360 initWithTitle:(package == nil ? id : [package name])
3361 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3362 defaultButtonIndex:0
3367 [sheet setBodyText:error];
3368 [sheet popupAlertAnimated:YES];
3371 - (void) setProgressTitle:(NSString *)title {
3373 performSelectorOnMainThread:@selector(_setProgressTitle:)
3379 - (void) setProgressPercent:(float)percent {
3381 performSelectorOnMainThread:@selector(_setProgressPercent:)
3382 withObject:[NSNumber numberWithFloat:percent]
3387 - (void) startProgress {
3388 last_ = [NSDate timeIntervalSinceReferenceDate];
3391 - (void) addProgressOutput:(NSString *)output {
3393 performSelectorOnMainThread:@selector(_addProgressOutput:)
3399 - (bool) isCancelling:(size_t)received {
3401 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3402 if (received_ != received) {
3403 received_ = received;
3405 } else if (now - last_ > 30)
3412 - (void) _setConfigurationData:(NSString *)data {
3413 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3415 _assert(conffile_r(data));
3417 NSString *ofile = conffile_r[1];
3418 //NSString *nfile = conffile_r[2];
3420 UIActionSheet *sheet = [[[UIActionSheet alloc]
3421 initWithTitle:@"Configuration Upgrade"
3422 buttons:[NSArray arrayWithObjects:
3423 @"Keep My Old Copy",
3424 @"Accept The New Copy",
3425 // XXX: @"See What Changed",
3427 defaultButtonIndex:0
3432 [sheet setBodyText:[NSString stringWithFormat:
3433 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3436 [sheet popupAlertAnimated:YES];
3439 - (void) _setProgressTitle:(NSString *)title {
3440 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3441 for (size_t i(0), e([words count]); i != e; ++i) {
3442 NSString *word([words objectAtIndex:i]);
3443 if (Package *package = [database_ packageWithName:word])
3444 [words replaceObjectAtIndex:i withObject:[package name]];
3447 [status_ setText:[words componentsJoinedByString:@" "]];
3450 - (void) _setProgressPercent:(NSNumber *)percent {
3451 [progress_ setProgress:[percent floatValue]];
3454 - (void) _addProgressOutput:(NSString *)output {
3455 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3456 CGSize size = [output_ contentSize];
3457 CGRect rect = {{0, size.height}, {size.width, 0}};
3458 [output_ scrollRectToVisible:rect animated:YES];
3461 - (BOOL) isRunning {
3468 /* Package Cell {{{ */
3469 @interface PackageCell : UISimpleTableCell {
3472 NSString *description_;
3476 UITextLabel *status_;
3480 - (PackageCell *) init;
3481 - (void) setPackage:(Package *)package;
3483 + (int) heightForPackage:(Package *)package;
3487 @implementation PackageCell
3489 - (void) clearPackage {
3500 if (description_ != nil) {
3501 [description_ release];
3505 if (source_ != nil) {
3510 if (badge_ != nil) {
3517 [self clearPackage];
3524 - (PackageCell *) init {
3525 if ((self = [super init]) != nil) {
3527 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3528 [status_ setBackgroundColor:[UIColor clearColor]];
3529 [status_ setFont:small];
3534 - (void) setPackage:(Package *)package {
3535 [self clearPackage];
3537 Source *source = [package source];
3538 NSString *section = [package simpleSection];
3540 icon_ = [[package icon] retain];
3542 name_ = [[package name] retain];
3543 description_ = [[package tagline] retain];
3545 NSString *label = nil;
3546 bool trusted = false;
3548 if (source != nil) {
3549 label = [source label];
3550 trusted = [source trusted];
3551 } else if ([[package id] isEqualToString:@"firmware"])
3554 label = @"Unknown/Local";
3556 NSString *from = [NSString stringWithFormat:@"from %@", label];
3558 if (section != nil && ![section isEqualToString:label])
3559 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3561 source_ = [from retain];
3563 if (NSString *purpose = [package primaryPurpose])
3564 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3565 badge_ = [badge_ retain];
3568 if (NSString *mode = [package mode]) {
3569 [badge_ setImage:[UIImage applicationImageNamed:
3570 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3573 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3574 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3575 } else if ([package half]) {
3576 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3577 [status_ setText:@"Package Damaged"];
3578 [status_ setColor:[UIColor redColor]];
3580 [badge_ setImage:nil];
3581 [status_ setText:nil];
3586 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3589 rect.size = [icon_ size];
3591 rect.size.width /= 2;
3592 rect.size.height /= 2;
3594 rect.origin.x = 25 - rect.size.width / 2;
3595 rect.origin.y = 25 - rect.size.height / 2;
3597 [icon_ drawInRect:rect];
3600 if (badge_ != nil) {
3601 CGSize size = [badge_ size];
3603 [badge_ drawAtPoint:CGPointMake(
3604 36 - size.width / 2,
3605 36 - size.height / 2
3614 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3615 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3619 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3621 [super drawContentInRect:rect selected:selected];
3624 + (int) heightForPackage:(Package *)package {
3625 NSString *tagline([package tagline]);
3626 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3628 if ([package hasMode] || [package half])
3637 /* Section Cell {{{ */
3638 @interface SectionCell : UISimpleTableCell {
3643 _UISwitchSlider *switch_;
3648 - (void) setSection:(Section *)section editing:(BOOL)editing;
3652 @implementation SectionCell
3654 - (void) clearSection {
3655 if (section_ != nil) {
3665 if (count_ != nil) {
3672 [self clearSection];
3679 if ((self = [super init]) != nil) {
3680 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3682 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3683 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3687 - (void) onSwitch:(id)sender {
3688 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3689 if (metadata == nil) {
3690 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3691 [Sections_ setObject:metadata forKey:section_];
3695 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3698 - (void) setSection:(Section *)section editing:(BOOL)editing {
3699 if (editing != editing_) {
3701 [switch_ removeFromSuperview];
3703 [self addSubview:switch_];
3707 [self clearSection];
3709 if (section == nil) {
3710 name_ = [@"All Packages" retain];
3713 section_ = [section name];
3714 if (section_ != nil)
3715 section_ = [section_ retain];
3716 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3717 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3720 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3724 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3725 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3732 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3734 CGSize size = [count_ sizeWithFont:Font14_];
3738 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3740 [super drawContentInRect:rect selected:selected];
3746 /* File Table {{{ */
3747 @interface FileTable : RVPage {
3748 _transient Database *database_;
3751 NSMutableArray *files_;
3755 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3756 - (void) setPackage:(Package *)package;
3760 @implementation FileTable
3763 if (package_ != nil)
3772 - (int) numberOfRowsInTable:(UITable *)table {
3773 return files_ == nil ? 0 : [files_ count];
3776 - (float) table:(UITable *)table heightForRow:(int)row {
3780 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3781 if (reusing == nil) {
3782 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3783 UIFont *font = [UIFont systemFontOfSize:16];
3784 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3786 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3790 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3794 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3795 if ((self = [super initWithBook:book]) != nil) {
3796 database_ = database;
3798 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3800 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3801 [self addSubview:list_];
3803 UITableColumn *column = [[[UITableColumn alloc]
3804 initWithTitle:@"Name"
3806 width:[self frame].size.width
3809 [list_ setDataSource:self];
3810 [list_ setSeparatorStyle:1];
3811 [list_ addTableColumn:column];
3812 [list_ setDelegate:self];
3813 [list_ setReusesTableCells:YES];
3817 - (void) setPackage:(Package *)package {
3818 if (package_ != nil) {
3819 [package_ autorelease];
3828 [files_ removeAllObjects];
3830 if (package != nil) {
3831 package_ = [package retain];
3832 name_ = [[package id] retain];
3834 if (NSArray *files = [package files])
3835 [files_ addObjectsFromArray:files];
3837 if ([files_ count] != 0) {
3838 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3839 [files_ removeObjectAtIndex:0];
3840 [files_ sortUsingSelector:@selector(compareByPath:)];
3842 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3843 [stack addObject:@"/"];
3845 for (int i(0), e([files_ count]); i != e; ++i) {
3846 NSString *file = [files_ objectAtIndex:i];
3847 while (![file hasPrefix:[stack lastObject]])
3848 [stack removeLastObject];
3849 NSString *directory = [stack lastObject];
3850 [stack addObject:[file stringByAppendingString:@"/"]];
3851 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3852 ([stack count] - 2) * 3, "",
3853 [file substringFromIndex:[directory length]]
3862 - (void) resetViewAnimated:(BOOL)animated {
3863 [list_ resetViewAnimated:animated];
3866 - (void) reloadData {
3867 [self setPackage:[database_ packageWithName:name_]];
3868 [self reloadButtons];
3871 - (NSString *) title {
3872 return @"Installed Files";
3875 - (NSString *) backButtonTitle {
3881 /* Package View {{{ */
3882 @interface PackageView : BrowserView {
3883 _transient Database *database_;
3886 NSMutableArray *buttons_;
3889 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3890 - (void) setPackage:(Package *)package;
3894 @implementation PackageView
3897 if (package_ != nil)
3905 - (void) _clickButtonWithName:(NSString *)name {
3906 if ([name isEqualToString:@"Install"])
3907 [delegate_ installPackage:package_];
3908 else if ([name isEqualToString:@"Reinstall"])
3909 [delegate_ installPackage:package_];
3910 else if ([name isEqualToString:@"Remove"])
3911 [delegate_ removePackage:package_];
3912 else if ([name isEqualToString:@"Upgrade"])
3913 [delegate_ installPackage:package_];
3914 else _assert(false);
3917 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3918 NSString *context([sheet context]);
3920 if ([context isEqualToString:@"modify"]) {
3921 int count = [buttons_ count];
3922 _assert(count != 0);
3923 _assert(button <= count + 1);
3925 if (count != button - 1)
3926 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3930 [super alertSheet:sheet buttonClicked:button];
3933 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3934 return [super webView:sender didFinishLoadForFrame:frame];
3937 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3938 [window setValue:package_ forKey:@"package"];
3939 [super webView:sender didClearWindowObject:window forFrame:frame];
3943 - (void) _rightButtonClicked {
3944 /*[super _rightButtonClicked];
3947 int count = [buttons_ count];
3948 _assert(count != 0);
3951 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3953 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3954 [buttons addObjectsFromArray:buttons_];
3955 [buttons addObject:@"Cancel"];
3957 [delegate_ slideUp:[[[UIActionSheet alloc]
3960 defaultButtonIndex:2
3968 - (id) _rightButtonTitle {
3969 int count = [buttons_ count];
3970 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3973 - (NSString *) backButtonTitle {
3977 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3978 if ((self = [super initWithBook:book]) != nil) {
3979 database_ = database;
3980 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3984 - (void) setPackage:(Package *)package {
3985 if (package_ != nil) {
3986 [package_ autorelease];
3995 [buttons_ removeAllObjects];
3997 if (package != nil) {
3998 package_ = [package retain];
3999 name_ = [[package id] retain];
4001 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4003 if ([package_ source] == nil);
4004 else if ([package_ upgradableAndEssential:NO])
4005 [buttons_ addObject:@"Upgrade"];
4006 else if ([package_ installed] == nil)
4007 [buttons_ addObject:@"Install"];
4009 [buttons_ addObject:@"Reinstall"];
4010 if ([package_ installed] != nil)
4011 [buttons_ addObject:@"Remove"];
4019 - (void) reloadData {
4020 [self setPackage:[database_ packageWithName:name_]];
4021 [self reloadButtons];
4026 /* Package Table {{{ */
4027 @interface PackageTable : RVPage {
4028 _transient Database *database_;
4030 NSMutableArray *packages_;
4031 NSMutableArray *sections_;
4032 UISectionList *list_;
4035 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4037 - (void) setDelegate:(id)delegate;
4039 - (void) reloadData;
4040 - (void) resetCursor;
4042 - (UISectionList *) list;
4044 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4048 @implementation PackageTable
4051 [list_ setDataSource:nil];
4054 [packages_ release];
4055 [sections_ release];
4060 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4061 return [sections_ count];
4064 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4065 return [[sections_ objectAtIndex:section] name];
4068 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4069 return [[sections_ objectAtIndex:section] row];
4072 - (int) numberOfRowsInTable:(UITable *)table {
4073 return [packages_ count];
4076 - (float) table:(UITable *)table heightForRow:(int)row {
4077 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4080 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4082 reusing = [[[PackageCell alloc] init] autorelease];
4083 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4087 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4091 - (void) tableRowSelected:(NSNotification *)notification {
4092 int row = [[notification object] selectedRow];
4096 Package *package = [packages_ objectAtIndex:row];
4097 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4098 [view setDelegate:delegate_];
4099 [view setPackage:package];
4100 [book_ pushPage:view];
4103 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4104 if ((self = [super initWithBook:book]) != nil) {
4105 database_ = database;
4106 title_ = [title retain];
4108 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4109 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4111 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4112 [list_ setDataSource:self];
4114 UITableColumn *column = [[[UITableColumn alloc]
4115 initWithTitle:@"Name"
4117 width:[self frame].size.width
4120 UITable *table = [list_ table];
4121 [table setSeparatorStyle:1];
4122 [table addTableColumn:column];
4123 [table setDelegate:self];
4124 [table setReusesTableCells:YES];
4126 [self addSubview:list_];
4129 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4130 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4134 - (void) setDelegate:(id)delegate {
4135 delegate_ = delegate;
4138 - (bool) hasPackage:(Package *)package {
4142 - (void) reloadData {
4143 NSArray *packages = [database_ packages];
4145 [packages_ removeAllObjects];
4146 [sections_ removeAllObjects];
4148 for (size_t i(0); i != [packages count]; ++i) {
4149 Package *package([packages objectAtIndex:i]);
4150 if ([self hasPackage:package])
4151 [packages_ addObject:package];
4154 Section *section = nil;
4156 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4157 Package *package = [packages_ objectAtIndex:offset];
4158 NSString *name = [package index];
4160 if (section == nil || ![[section name] isEqualToString:name]) {
4161 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4162 [sections_ addObject:section];
4165 [section addToCount];
4171 - (NSString *) title {
4175 - (void) resetViewAnimated:(BOOL)animated {
4176 [list_ resetViewAnimated:animated];
4179 - (void) resetCursor {
4180 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4183 - (UISectionList *) list {
4187 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4188 [list_ setShouldHideHeaderInShortLists:hide];
4193 /* Filtered Package Table {{{ */
4194 @interface FilteredPackageTable : PackageTable {
4199 - (void) setObject:(id)object;
4201 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4205 @implementation FilteredPackageTable
4213 - (void) setObject:(id)object {
4219 object_ = [object retain];
4222 - (bool) hasPackage:(Package *)package {
4223 return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
4226 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4227 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4229 object_ = object == nil ? nil : [object retain];
4236 /* Add Source View {{{ */
4237 @interface AddSourceView : RVPage {
4238 _transient Database *database_;
4241 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4245 @implementation AddSourceView
4247 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4248 if ((self = [super initWithBook:book]) != nil) {
4249 database_ = database;
4255 /* Source Cell {{{ */
4256 @interface SourceCell : UITableCell {
4259 NSString *description_;
4265 - (SourceCell *) initWithSource:(Source *)source;
4269 @implementation SourceCell
4274 [description_ release];
4279 - (SourceCell *) initWithSource:(Source *)source {
4280 if ((self = [super init]) != nil) {
4282 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4284 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4285 icon_ = [icon_ retain];
4287 origin_ = [[source name] retain];
4288 label_ = [[source uri] retain];
4289 description_ = [[source description] retain];
4293 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4295 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4302 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4306 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4310 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4312 [super drawContentInRect:rect selected:selected];
4317 /* Source Table {{{ */
4318 @interface SourceTable : RVPage {
4319 _transient Database *database_;
4320 UISectionList *list_;
4321 NSMutableArray *sources_;
4322 UIActionSheet *alert_;
4326 UIProgressHUD *hud_;
4329 //NSURLConnection *installer_;
4330 NSURLConnection *trivial_bz2_;
4331 NSURLConnection *trivial_gz_;
4332 //NSURLConnection *automatic_;
4337 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4341 @implementation SourceTable
4343 - (void) _deallocConnection:(NSURLConnection *)connection {
4344 if (connection != nil) {
4345 [connection cancel];
4346 //[connection setDelegate:nil];
4347 [connection release];
4352 [[list_ table] setDelegate:nil];
4353 [list_ setDataSource:nil];
4362 //[self _deallocConnection:installer_];
4363 [self _deallocConnection:trivial_gz_];
4364 [self _deallocConnection:trivial_bz2_];
4365 //[self _deallocConnection:automatic_];
4372 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4373 return offset_ == 0 ? 1 : 2;
4376 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4377 switch (section + (offset_ == 0 ? 1 : 0)) {
4378 case 0: return @"Entered by User";
4379 case 1: return @"Installed by Packages";
4387 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4388 switch (section + (offset_ == 0 ? 1 : 0)) {
4390 case 1: return offset_;
4398 - (int) numberOfRowsInTable:(UITable *)table {
4399 return [sources_ count];
4402 - (float) table:(UITable *)table heightForRow:(int)row {
4403 Source *source = [sources_ objectAtIndex:row];
4404 return [source description] == nil ? 56 : 73;
4407 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4408 Source *source = [sources_ objectAtIndex:row];
4409 // XXX: weird warning, stupid selectors ;P
4410 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4413 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4417 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4421 - (void) tableRowSelected:(NSNotification*)notification {
4422 UITable *table([list_ table]);
4423 int row([table selectedRow]);
4427 Source *source = [sources_ objectAtIndex:row];
4429 PackageTable *packages = [[[FilteredPackageTable alloc]
4432 title:[source label]
4433 filter:@selector(isVisibleInSource:)
4437 [packages setDelegate:delegate_];
4439 [book_ pushPage:packages];
4442 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4443 Source *source = [sources_ objectAtIndex:row];
4444 return [source record] != nil;
4447 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4448 [[list_ table] setDeleteConfirmationRow:row];
4451 - (void) table:(UITable *)table deleteRow:(int)row {
4452 Source *source = [sources_ objectAtIndex:row];
4453 [Sources_ removeObjectForKey:[source key]];
4454 [delegate_ syncData];
4457 - (void) _endConnection:(NSURLConnection *)connection {
4458 NSURLConnection **field = NULL;
4459 if (connection == trivial_bz2_)
4460 field = &trivial_bz2_;
4461 else if (connection == trivial_gz_)
4462 field = &trivial_gz_;
4463 _assert(field != NULL);
4464 [connection release];
4468 trivial_bz2_ == nil &&
4471 [delegate_ setStatusBarShowsProgress:NO];
4474 [hud_ removeFromSuperview];
4479 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4482 @"./", @"Distribution",
4483 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4485 [delegate_ syncData];
4486 } else if (error_ != nil) {
4487 UIActionSheet *sheet = [[[UIActionSheet alloc]
4488 initWithTitle:@"Verification Error"
4489 buttons:[NSArray arrayWithObjects:@"OK", nil]
4490 defaultButtonIndex:0
4495 [sheet setBodyText:[error_ localizedDescription]];
4496 [sheet popupAlertAnimated:YES];
4498 UIActionSheet *sheet = [[[UIActionSheet alloc]
4499 initWithTitle:@"Did not Find Repository"
4500 buttons:[NSArray arrayWithObjects:@"OK", nil]
4501 defaultButtonIndex:0
4506 [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."];
4507 [sheet popupAlertAnimated:YES];
4513 if (error_ != nil) {
4520 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4521 switch ([response statusCode]) {
4527 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4528 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4530 error_ = [error retain];
4531 [self _endConnection:connection];
4534 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4535 [self _endConnection:connection];
4538 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4539 NSMutableURLRequest *request = [NSMutableURLRequest
4540 requestWithURL:[NSURL URLWithString:href]
4541 cachePolicy:NSURLRequestUseProtocolCachePolicy
4542 timeoutInterval:20.0
4545 [request setHTTPMethod:method];
4547 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4550 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4551 NSString *context([sheet context]);
4553 if ([context isEqualToString:@"source"]) {
4556 NSString *href = [[sheet textField] text];
4558 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4560 if (![href hasSuffix:@"/"])
4561 href_ = [href stringByAppendingString:@"/"];
4564 href_ = [href_ retain];
4566 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4567 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4568 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4572 hud_ = [delegate_ addProgressHUD];
4573 [hud_ setText:@"Verifying URL"];
4584 } else if ([context isEqualToString:@"trivial"])
4586 else if ([context isEqualToString:@"urlerror"])
4590 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4591 if ((self = [super initWithBook:book]) != nil) {
4592 database_ = database;
4593 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4595 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4596 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4597 [list_ setShouldHideHeaderInShortLists:NO];
4599 [self addSubview:list_];
4600 [list_ setDataSource:self];
4602 UITableColumn *column = [[UITableColumn alloc]
4603 initWithTitle:@"Name"
4605 width:[self frame].size.width
4608 UITable *table = [list_ table];
4609 [table setSeparatorStyle:1];
4610 [table addTableColumn:column];
4611 [table setDelegate:self];
4615 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4616 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4620 - (void) reloadData {
4622 _assert(list.ReadMainList());
4624 [sources_ removeAllObjects];
4625 [sources_ addObjectsFromArray:[database_ sources]];
4627 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4630 int count = [sources_ count];
4631 for (offset_ = 0; offset_ != count; ++offset_) {
4632 Source *source = [sources_ objectAtIndex:offset_];
4633 if ([source record] == nil)
4640 - (void) resetViewAnimated:(BOOL)animated {
4641 [list_ resetViewAnimated:animated];
4644 - (void) _leftButtonClicked {
4645 /*[book_ pushPage:[[[AddSourceView alloc]
4650 UIActionSheet *sheet = [[[UIActionSheet alloc]
4651 initWithTitle:@"Enter Cydia/APT URL"
4652 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4653 defaultButtonIndex:0
4658 [sheet setNumberOfRows:1];
4660 [sheet addTextFieldWithValue:@"http://" label:@""];
4662 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4663 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4664 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4665 [traits setKeyboardType:UIKeyboardTypeURL];
4666 // XXX: UIReturnKeyDone
4667 [traits setReturnKeyType:UIReturnKeyNext];
4669 [sheet popupAlertAnimated:YES];
4672 - (void) _rightButtonClicked {
4673 UITable *table = [list_ table];
4674 BOOL editing = [table isRowDeletionEnabled];
4675 [table enableRowDeletion:!editing animated:YES];
4676 [book_ reloadButtonsForPage:self];
4679 - (NSString *) title {
4683 - (NSString *) leftButtonTitle {
4684 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4687 - (id) rightButtonTitle {
4688 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4691 - (UINavigationButtonStyle) rightButtonStyle {
4692 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4698 /* Installed View {{{ */
4699 @interface InstalledView : RVPage {
4700 _transient Database *database_;
4701 FilteredPackageTable *packages_;
4705 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4709 @implementation InstalledView
4712 [packages_ release];
4716 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4717 if ((self = [super initWithBook:book]) != nil) {
4718 database_ = database;
4720 packages_ = [[FilteredPackageTable alloc]
4724 filter:@selector(isInstalledAndVisible:)
4725 with:[NSNumber numberWithBool:YES]
4728 [self addSubview:packages_];
4730 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4731 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4735 - (void) resetViewAnimated:(BOOL)animated {
4736 [packages_ resetViewAnimated:animated];
4739 - (void) reloadData {
4740 [packages_ reloadData];
4743 - (void) _rightButtonClicked {
4744 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4745 [packages_ reloadData];
4747 [book_ reloadButtonsForPage:self];
4750 - (NSString *) title {
4751 return @"Installed";
4754 - (NSString *) backButtonTitle {
4758 - (id) rightButtonTitle {
4759 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4762 - (UINavigationButtonStyle) rightButtonStyle {
4763 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4766 - (void) setDelegate:(id)delegate {
4767 [super setDelegate:delegate];
4768 [packages_ setDelegate:delegate];
4775 @interface HomeView : BrowserView {
4780 @implementation HomeView
4782 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4783 NSString *context([sheet context]);
4785 if ([context isEqualToString:@"about"])
4788 [super alertSheet:sheet buttonClicked:button];
4791 - (void) _leftButtonClicked {
4792 UIActionSheet *sheet = [[[UIActionSheet alloc]
4793 initWithTitle:@"About Cydia Installer"
4794 buttons:[NSArray arrayWithObjects:@"Close", nil]
4795 defaultButtonIndex:0
4801 @"Copyright (C) 2008\n"
4802 "Jay Freeman (saurik)\n"
4803 "saurik@saurik.com\n"
4804 "http://www.saurik.com/\n"
4807 "http://www.theokorigroup.com/\n"
4809 "College of Creative Studies,\n"
4810 "University of California,\n"
4812 "http://www.ccs.ucsb.edu/"
4815 [sheet popupAlertAnimated:YES];
4818 - (NSString *) leftButtonTitle {
4824 /* Manage View {{{ */
4825 @interface ManageView : BrowserView {
4830 @implementation ManageView
4832 - (NSString *) title {
4836 - (void) _leftButtonClicked {
4837 [delegate_ askForSettings];
4840 - (NSString *) leftButtonTitle {
4845 - (id) _rightButtonTitle {
4857 /* Indirect Delegate {{{ */
4858 @interface IndirectDelegate : NSProxy {
4859 _transient volatile id delegate_;
4862 - (void) setDelegate:(id)delegate;
4863 - (id) initWithDelegate:(id)delegate;
4866 @implementation IndirectDelegate
4868 - (void) setDelegate:(id)delegate {
4869 delegate_ = delegate;
4872 - (id) initWithDelegate:(id)delegate {
4873 delegate_ = delegate;
4877 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4878 if (delegate_ != nil)
4879 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4881 // XXX: I fucking hate Apple so very very bad
4882 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4885 - (void) forwardInvocation:(NSInvocation *)inv {
4886 SEL sel = [inv selector];
4887 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4888 [inv invokeWithTarget:delegate_];
4894 #include <BrowserView.m>
4896 /* Cydia Book {{{ */
4897 @interface CYBook : RVBook <
4900 _transient Database *database_;
4901 UINavigationBar *overlay_;
4902 UINavigationBar *underlay_;
4903 UIProgressIndicator *indicator_;
4904 UITextLabel *prompt_;
4905 UIProgressBar *progress_;
4906 UINavigationButton *cancel_;
4909 NSTimeInterval last_;
4912 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4918 @implementation CYBook
4922 [indicator_ release];
4924 [progress_ release];
4929 - (NSString *) getTitleForPage:(RVPage *)page {
4930 return Simplify([super getTitleForPage:page]);
4938 [UIView beginAnimations:nil context:NULL];
4940 CGRect ovrframe = [overlay_ frame];
4941 ovrframe.origin.y = 0;
4942 [overlay_ setFrame:ovrframe];
4944 CGRect barframe = [navbar_ frame];
4945 barframe.origin.y += ovrframe.size.height;
4946 [navbar_ setFrame:barframe];
4948 CGRect trnframe = [transition_ frame];
4949 trnframe.origin.y += ovrframe.size.height;
4950 trnframe.size.height -= ovrframe.size.height;
4951 [transition_ setFrame:trnframe];
4953 [UIView endAnimations];
4955 [indicator_ startAnimation];
4956 [prompt_ setText:@"Updating Database"];
4957 [progress_ setProgress:0];
4960 last_ = [NSDate timeIntervalSinceReferenceDate];
4962 [overlay_ addSubview:cancel_];
4965 detachNewThreadSelector:@selector(_update)
4974 [indicator_ stopAnimation];
4976 [UIView beginAnimations:nil context:NULL];
4978 CGRect ovrframe = [overlay_ frame];
4979 ovrframe.origin.y = -ovrframe.size.height;
4980 [overlay_ setFrame:ovrframe];
4982 CGRect barframe = [navbar_ frame];
4983 barframe.origin.y -= ovrframe.size.height;
4984 [navbar_ setFrame:barframe];
4986 CGRect trnframe = [transition_ frame];
4987 trnframe.origin.y -= ovrframe.size.height;
4988 trnframe.size.height += ovrframe.size.height;
4989 [transition_ setFrame:trnframe];
4991 [UIView commitAnimations];
4993 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4996 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4997 if ((self = [super initWithFrame:frame]) != nil) {
4998 database_ = database;
5000 CGRect ovrrect = [navbar_ bounds];
5001 ovrrect.size.height = [UINavigationBar defaultSize].height;
5002 ovrrect.origin.y = -ovrrect.size.height;
5004 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5005 [self addSubview:overlay_];
5007 ovrrect.origin.y = frame.size.height;
5008 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5009 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5010 [self addSubview:underlay_];
5012 [overlay_ setBarStyle:1];
5013 [underlay_ setBarStyle:1];
5015 int barstyle = [overlay_ _barStyle:NO];
5016 bool ugly = barstyle == 0;
5018 UIProgressIndicatorStyle style = ugly ?
5019 UIProgressIndicatorStyleMediumBrown :
5020 UIProgressIndicatorStyleMediumWhite;
5022 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5023 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5024 CGRect indrect = {{indoffset, indoffset}, indsize};
5026 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5027 [indicator_ setStyle:style];
5028 [overlay_ addSubview:indicator_];
5030 CGSize prmsize = {215, indsize.height + 4};
5033 indoffset * 2 + indsize.width,
5037 unsigned(ovrrect.size.height - prmsize.height) / 2
5040 UIFont *font = [UIFont systemFontOfSize:15];
5042 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5044 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5045 [prompt_ setBackgroundColor:[UIColor clearColor]];
5046 [prompt_ setFont:font];
5048 [overlay_ addSubview:prompt_];
5050 CGSize prgsize = {75, 100};
5053 ovrrect.size.width - prgsize.width - 10,
5054 (ovrrect.size.height - prgsize.height) / 2
5057 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5058 [progress_ setStyle:0];
5059 [overlay_ addSubview:progress_];
5061 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5062 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5064 CGRect frame = [cancel_ frame];
5065 frame.size.width = 65;
5066 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5067 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5068 [cancel_ setFrame:frame];
5070 [cancel_ setBarStyle:barstyle];
5074 - (void) _onCancel {
5076 [cancel_ removeFromSuperview];
5079 - (void) _update { _pooled
5081 status.setDelegate(self);
5083 [database_ updateWithStatus:status];
5086 performSelectorOnMainThread:@selector(_update_)
5092 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5093 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5096 - (void) setProgressTitle:(NSString *)title {
5098 performSelectorOnMainThread:@selector(_setProgressTitle:)
5104 - (void) setProgressPercent:(float)percent {
5106 performSelectorOnMainThread:@selector(_setProgressPercent:)
5107 withObject:[NSNumber numberWithFloat:percent]
5112 - (void) startProgress {
5115 - (void) addProgressOutput:(NSString *)output {
5117 performSelectorOnMainThread:@selector(_addProgressOutput:)
5123 - (bool) isCancelling:(size_t)received {
5124 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5125 if (received_ != received) {
5126 received_ = received;
5128 } else if (now - last_ > 15)
5133 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5137 - (void) _setProgressTitle:(NSString *)title {
5138 [prompt_ setText:title];
5141 - (void) _setProgressPercent:(NSNumber *)percent {
5142 [progress_ setProgress:[percent floatValue]];
5145 - (void) _addProgressOutput:(NSString *)output {
5150 /* Cydia:// Protocol {{{ */
5151 @interface CydiaURLProtocol : NSURLProtocol {
5156 @implementation CydiaURLProtocol
5158 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5159 NSURL *url([request URL]);
5162 NSString *scheme([[url scheme] lowercaseString]);
5163 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5168 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5172 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5173 id<NSURLProtocolClient> client([self client]);
5174 NSData *data(UIImagePNGRepresentation(icon));
5176 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5177 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5178 [client URLProtocol:self didLoadData:data];
5179 [client URLProtocolDidFinishLoading:self];
5182 - (void) startLoading {
5183 id<NSURLProtocolClient> client([self client]);
5184 NSURLRequest *request([self request]);
5186 NSURL *url([request URL]);
5187 NSString *href([url absoluteString]);
5189 NSString *path([href substringFromIndex:8]);
5190 NSRange slash([path rangeOfString:@"/"]);
5193 if (slash.location == NSNotFound) {
5197 command = [path substringToIndex:slash.location];
5198 path = [path substringFromIndex:(slash.location + 1)];
5201 Database *database([Database sharedInstance]);
5203 if ([command isEqualToString:@"package-icon"]) {
5206 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5207 Package *package([database packageWithName:path]);
5210 UIImage *icon([package icon]);
5211 [self _returnPNGWithImage:icon forRequest:request];
5212 } else if ([command isEqualToString:@"source-icon"]) {
5215 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5216 NSString *source(Simplify(path));
5217 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5219 icon = [UIImage applicationImageNamed:@"unknown.png"];
5220 [self _returnPNGWithImage:icon forRequest:request];
5221 } else if ([command isEqualToString:@"uikit-image"]) {
5224 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5225 UIImage *icon(_UIImageWithName(path));
5226 [self _returnPNGWithImage:icon forRequest:request];
5227 } else if ([command isEqualToString:@"section-icon"]) {
5230 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5231 NSString *section(Simplify(path));
5232 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5234 icon = [UIImage applicationImageNamed:@"unknown.png"];
5235 [self _returnPNGWithImage:icon forRequest:request];
5237 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5241 - (void) stopLoading {
5247 /* Sections View {{{ */
5248 @interface SectionsView : RVPage {
5249 _transient Database *database_;
5250 NSMutableArray *sections_;
5251 NSMutableArray *filtered_;
5252 UITransitionView *transition_;
5258 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5259 - (void) reloadData;
5264 @implementation SectionsView
5267 [list_ setDataSource:nil];
5268 [list_ setDelegate:nil];
5270 [sections_ release];
5271 [filtered_ release];
5272 [transition_ release];
5274 [accessory_ release];
5278 - (int) numberOfRowsInTable:(UITable *)table {
5279 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5282 - (float) table:(UITable *)table heightForRow:(int)row {
5286 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5288 reusing = [[[SectionCell alloc] init] autorelease];
5289 [(SectionCell *)reusing setSection:(editing_ ?
5290 [sections_ objectAtIndex:row] :
5291 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5292 ) editing:editing_];
5296 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5300 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5304 - (void) tableRowSelected:(NSNotification *)notification {
5305 int row = [[notification object] selectedRow];
5316 title = @"All Packages";
5318 section = [filtered_ objectAtIndex:(row - 1)];
5319 name = [section name];
5325 title = @"(No Section)";
5329 PackageTable *table = [[[FilteredPackageTable alloc]
5333 filter:@selector(isVisiblyUninstalledInSection:)
5337 [table setDelegate:delegate_];
5339 [book_ pushPage:table];
5342 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5343 if ((self = [super initWithBook:book]) != nil) {
5344 database_ = database;
5346 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5347 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5349 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5350 [self addSubview:transition_];
5352 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5353 [transition_ transition:0 toView:list_];
5355 UITableColumn *column = [[[UITableColumn alloc]
5356 initWithTitle:@"Name"
5358 width:[self frame].size.width
5361 [list_ setDataSource:self];
5362 [list_ setSeparatorStyle:1];
5363 [list_ addTableColumn:column];
5364 [list_ setDelegate:self];
5365 [list_ setReusesTableCells:YES];
5369 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5370 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5374 - (void) reloadData {
5375 NSArray *packages = [database_ packages];
5377 [sections_ removeAllObjects];
5378 [filtered_ removeAllObjects];
5380 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5381 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5384 for (size_t i(0); i != [packages count]; ++i) {
5385 Package *package([packages objectAtIndex:i]);
5386 NSString *name([package section]);
5389 Section *section([sections objectForKey:name]);
5390 if (section == nil) {
5391 section = [[[Section alloc] initWithName:name] autorelease];
5392 [sections setObject:section forKey:name];
5396 if ([package valid] && [package installed] == nil && [package visible])
5397 [filtered addObject:package];
5401 [sections_ addObjectsFromArray:[sections allValues]];
5402 [sections_ sortUsingSelector:@selector(compareByName:)];
5405 [filtered sortUsingSelector:@selector(compareBySection:)];
5408 Section *section = nil;
5409 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5410 Package *package = [filtered objectAtIndex:offset];
5411 NSString *name = [package section];
5413 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5414 section = name == nil ?
5415 [[[Section alloc] initWithName:nil] autorelease] :
5416 [sections objectForKey:name];
5417 [filtered_ addObject:section];
5420 [section addToCount];
5428 - (void) resetView {
5430 [self _rightButtonClicked];
5433 - (void) resetViewAnimated:(BOOL)animated {
5434 [list_ resetViewAnimated:animated];
5437 - (void) _rightButtonClicked {
5438 if ((editing_ = !editing_))
5441 [delegate_ updateData];
5442 [book_ reloadTitleForPage:self];
5443 [book_ reloadButtonsForPage:self];
5446 - (NSString *) title {
5447 return editing_ ? @"Section Visibility" : @"Install by Section";
5450 - (NSString *) backButtonTitle {
5454 - (id) rightButtonTitle {
5455 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5458 - (UINavigationButtonStyle) rightButtonStyle {
5459 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5462 - (UIView *) accessoryView {
5468 /* Changes View {{{ */
5469 @interface ChangesView : RVPage {
5470 _transient Database *database_;
5471 NSMutableArray *packages_;
5472 NSMutableArray *sections_;
5473 UISectionList *list_;
5477 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5478 - (void) reloadData;
5482 @implementation ChangesView
5485 [[list_ table] setDelegate:nil];
5486 [list_ setDataSource:nil];
5488 [packages_ release];
5489 [sections_ release];
5494 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5495 return [sections_ count];
5498 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5499 return [[sections_ objectAtIndex:section] name];
5502 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5503 return [[sections_ objectAtIndex:section] row];
5506 - (int) numberOfRowsInTable:(UITable *)table {
5507 return [packages_ count];
5510 - (float) table:(UITable *)table heightForRow:(int)row {
5511 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5514 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5516 reusing = [[[PackageCell alloc] init] autorelease];
5517 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5521 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5525 - (void) tableRowSelected:(NSNotification *)notification {
5526 int row = [[notification object] selectedRow];
5529 Package *package = [packages_ objectAtIndex:row];
5530 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5531 [view setDelegate:delegate_];
5532 [view setPackage:package];
5533 [book_ pushPage:view];
5536 - (void) _leftButtonClicked {
5537 [(CYBook *)book_ update];
5538 [self reloadButtons];
5541 - (void) _rightButtonClicked {
5542 [delegate_ distUpgrade];
5545 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5546 if ((self = [super initWithBook:book]) != nil) {
5547 database_ = database;
5549 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5550 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5552 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5553 [self addSubview:list_];
5555 [list_ setShouldHideHeaderInShortLists:NO];
5556 [list_ setDataSource:self];
5557 //[list_ setSectionListStyle:1];
5559 UITableColumn *column = [[[UITableColumn alloc]
5560 initWithTitle:@"Name"
5562 width:[self frame].size.width
5565 UITable *table = [list_ table];
5566 [table setSeparatorStyle:1];
5567 [table addTableColumn:column];
5568 [table setDelegate:self];
5569 [table setReusesTableCells:YES];
5573 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5574 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5578 - (void) reloadData {
5579 NSArray *packages = [database_ packages];
5581 [packages_ removeAllObjects];
5582 [sections_ removeAllObjects];
5585 for (size_t i(0); i != [packages count]; ++i) {
5586 Package *package([packages objectAtIndex:i]);
5589 [package installed] == nil && [package valid] && [package visible] ||
5590 [package upgradableAndEssential:NO]
5592 [packages_ addObject:package];
5596 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5599 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5600 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5601 Section *section = nil;
5605 bool unseens = false;
5607 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5610 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5611 Package *package = [packages_ objectAtIndex:offset];
5613 if (![package upgradableAndEssential:YES]) {
5615 NSDate *seen = [package seen];
5617 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5620 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5621 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5622 [sections_ addObject:section];
5626 [section addToCount];
5627 } else if ([package ignored])
5628 [ignored addToCount];
5631 [upgradable addToCount];
5636 CFRelease(formatter);
5639 Section *last = [sections_ lastObject];
5640 size_t count = [last count];
5641 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5642 [sections_ removeLastObject];
5645 if ([ignored count] != 0)
5646 [sections_ insertObject:ignored atIndex:0];
5648 [sections_ insertObject:upgradable atIndex:0];
5651 [self reloadButtons];
5654 - (void) resetViewAnimated:(BOOL)animated {
5655 [list_ resetViewAnimated:animated];
5658 - (NSString *) leftButtonTitle {
5659 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5662 - (id) rightButtonTitle {
5663 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5666 - (NSString *) title {
5672 /* Search View {{{ */
5673 @protocol SearchViewDelegate
5674 - (void) showKeyboard:(BOOL)show;
5677 @interface SearchView : RVPage {
5679 UISearchField *field_;
5680 UITransitionView *transition_;
5681 FilteredPackageTable *table_;
5682 UIPreferencesTable *advanced_;
5688 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5689 - (void) reloadData;
5693 @implementation SearchView
5696 [field_ setDelegate:nil];
5698 [accessory_ release];
5700 [transition_ release];
5702 [advanced_ release];
5707 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5711 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5713 case 0: return @"Advanced Search (Coming Soon!)";
5715 default: _assert(false);
5719 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5723 default: _assert(false);
5727 - (void) _showKeyboard:(BOOL)show {
5728 CGSize keysize = [UIKeyboard defaultSize];
5729 CGRect keydown = [book_ pageBounds];
5730 CGRect keyup = keydown;
5731 keyup.size.height -= keysize.height - ButtonBarHeight_;
5733 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5735 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5736 [animation setSignificantRectFields:8];
5739 [animation setStartFrame:keydown];
5740 [animation setEndFrame:keyup];
5742 [animation setStartFrame:keyup];
5743 [animation setEndFrame:keydown];
5746 UIAnimator *animator = [UIAnimator sharedAnimator];
5749 addAnimations:[NSArray arrayWithObjects:animation, nil]
5750 withDuration:(KeyboardTime_ - delay)
5755 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5757 [delegate_ showKeyboard:show];
5760 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5761 [self _showKeyboard:YES];
5764 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5765 [self _showKeyboard:NO];
5768 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5770 NSString *text([field_ text]);
5771 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5777 - (void) textFieldClearButtonPressed:(UITextField *)field {
5781 - (void) keyboardInputShouldDelete:(id)input {
5785 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5786 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5790 [field_ resignFirstResponder];
5795 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5796 if ((self = [super initWithBook:book]) != nil) {
5797 CGRect pageBounds = [book_ pageBounds];
5799 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5800 [self addSubview:transition_];
5802 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5804 [advanced_ setReusesTableCells:YES];
5805 [advanced_ setDataSource:self];
5806 [advanced_ reloadData];
5808 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5809 CGColor dimmed(space_, 0, 0, 0, 0.5);
5810 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5812 table_ = [[FilteredPackageTable alloc]
5816 filter:@selector(isUnfilteredAndSearchedForBy:)
5820 [table_ setShouldHideHeaderInShortLists:NO];
5821 [transition_ transition:0 toView:table_];
5830 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5837 [self bounds].size.width - area.origin.x - 18;
5839 area.size.height = [UISearchField defaultHeight];
5841 field_ = [[UISearchField alloc] initWithFrame:area];
5843 UIFont *font = [UIFont systemFontOfSize:16];
5844 [field_ setFont:font];
5846 [field_ setPlaceholder:@"Package Names & Descriptions"];
5847 [field_ setDelegate:self];
5849 [field_ setPaddingTop:5];
5851 UITextInputTraits *traits([field_ textInputTraits]);
5852 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5853 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5854 [traits setReturnKeyType:UIReturnKeySearch];
5856 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5858 accessory_ = [[UIView alloc] initWithFrame:accrect];
5859 [accessory_ addSubview:field_];
5861 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5862 [configure setShowPressFeedback:YES];
5863 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5864 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5865 [accessory_ addSubview:configure];*/
5867 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5868 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5874 LKAnimation *animation = [LKTransition animation];
5875 [animation setType:@"oglFlip"];
5876 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5877 [animation setFillMode:@"extended"];
5878 [animation setTransitionFlags:3];
5879 [animation setDuration:10];
5880 [animation setSpeed:0.35];
5881 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5882 [[transition_ _layer] addAnimation:animation forKey:0];
5883 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5884 flipped_ = !flipped_;
5888 - (void) configurePushed {
5889 [field_ resignFirstResponder];
5893 - (void) resetViewAnimated:(BOOL)animated {
5896 [table_ resetViewAnimated:animated];
5899 - (void) _reloadData {
5902 - (void) reloadData {
5905 [table_ setObject:[field_ text]];
5906 [table_ reloadData];
5907 [table_ resetCursor];
5910 - (UIView *) accessoryView {
5914 - (NSString *) title {
5918 - (NSString *) backButtonTitle {
5922 - (void) setDelegate:(id)delegate {
5923 [table_ setDelegate:delegate];
5924 [super setDelegate:delegate];
5930 @interface SettingsView : RVPage {
5931 _transient Database *database_;
5934 UIPreferencesTable *table_;
5935 _UISwitchSlider *subscribedSwitch_;
5936 _UISwitchSlider *ignoredSwitch_;
5937 UIPreferencesControlTableCell *subscribedCell_;
5938 UIPreferencesControlTableCell *ignoredCell_;
5941 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5945 @implementation SettingsView
5948 [table_ setDataSource:nil];
5951 if (package_ != nil)
5954 [subscribedSwitch_ release];
5955 [ignoredSwitch_ release];
5956 [subscribedCell_ release];
5957 [ignoredCell_ release];
5961 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5962 if (package_ == nil)
5968 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5969 if (package_ == nil)
5976 default: _assert(false);
5982 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5983 if (package_ == nil)
5990 default: _assert(false);
5996 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5997 if (package_ == nil)
6004 default: _assert(false);
6010 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6011 if (package_ == nil)
6014 _UISwitchSlider *slider([cell control]);
6015 BOOL value([slider value] != 0);
6016 NSMutableDictionary *metadata([package_ metadata]);
6019 if (NSNumber *number = [metadata objectForKey:key])
6020 before = [number boolValue];
6024 if (value != before) {
6025 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6027 [delegate_ updateData];
6031 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6032 [self onSomething:cell withKey:@"IsSubscribed"];
6035 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6036 [self onSomething:cell withKey:@"IsIgnored"];
6039 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6040 if (package_ == nil)
6044 case 0: switch (row) {
6046 return subscribedCell_;
6048 return ignoredCell_;
6049 default: _assert(false);
6052 case 1: switch (row) {
6054 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6055 [cell setShowSelection:NO];
6056 [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
6060 default: _assert(false);
6063 default: _assert(false);
6069 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6070 if ((self = [super initWithBook:book])) {
6071 database_ = database;
6072 name_ = [package retain];
6074 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6075 [self addSubview:table_];
6077 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6078 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6080 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6081 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6083 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6084 [subscribedCell_ setShowSelection:NO];
6085 [subscribedCell_ setTitle:@"Show All Changes"];
6086 [subscribedCell_ setControl:subscribedSwitch_];
6088 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6089 [ignoredCell_ setShowSelection:NO];
6090 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6091 [ignoredCell_ setControl:ignoredSwitch_];
6093 [table_ setDataSource:self];
6098 - (void) resetViewAnimated:(BOOL)animated {
6099 [table_ resetViewAnimated:animated];
6102 - (void) reloadData {
6103 if (package_ != nil)
6104 [package_ autorelease];
6105 package_ = [database_ packageWithName:name_];
6106 if (package_ != nil) {
6108 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6109 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6112 [table_ reloadData];
6115 - (NSString *) title {
6121 /* Signature View {{{ */
6122 @interface SignatureView : BrowserView {
6123 _transient Database *database_;
6127 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6131 @implementation SignatureView
6138 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6140 [super webView:sender didClearWindowObject:window forFrame:frame];
6143 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6144 if ((self = [super initWithBook:book]) != nil) {
6145 database_ = database;
6146 package_ = [package retain];
6151 - (void) resetViewAnimated:(BOOL)animated {
6154 - (void) reloadData {
6155 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6161 @interface Cydia : UIApplication <
6162 ConfirmationViewDelegate,
6163 ProgressViewDelegate,
6172 UIToolbar *buttonbar_;
6176 NSMutableArray *essential_;
6177 NSMutableArray *broken_;
6179 Database *database_;
6180 ProgressView *progress_;
6184 UIKeyboard *keyboard_;
6185 UIProgressHUD *hud_;
6187 SectionsView *sections_;
6188 ChangesView *changes_;
6189 ManageView *manage_;
6190 SearchView *search_;
6195 @implementation Cydia
6198 if ([broken_ count] != 0) {
6199 int count = [broken_ count];
6201 UIActionSheet *sheet = [[[UIActionSheet alloc]
6202 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6203 buttons:[NSArray arrayWithObjects:
6205 @"Ignore (Temporary)",
6207 defaultButtonIndex:0
6212 [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."];
6213 [sheet popupAlertAnimated:YES];
6214 } else if (!Ignored_ && [essential_ count] != 0) {
6215 int count = [essential_ count];
6217 UIActionSheet *sheet = [[[UIActionSheet alloc]
6218 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6219 buttons:[NSArray arrayWithObjects:
6220 @"Upgrade Essential",
6221 @"Complete Upgrade",
6222 @"Ignore (Temporary)",
6224 defaultButtonIndex:0
6229 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6230 [sheet popupAlertAnimated:YES];
6234 - (void) _reloadData {
6235 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6236 [hud setText:@"Reloading Data"];
6237 [overlay_ addSubview:hud];
6240 [database_ reloadData];
6244 [essential_ removeAllObjects];
6245 [broken_ removeAllObjects];
6247 NSArray *packages = [database_ packages];
6248 for (Package *package in packages) {
6250 [broken_ addObject:package];
6251 if ([package upgradableAndEssential:NO]) {
6252 if ([package essential])
6253 [essential_ addObject:package];
6259 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6260 [buttonbar_ setBadgeValue:badge forButton:3];
6261 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6262 [buttonbar_ setBadgeAnimated:YES forButton:3];
6263 [self setApplicationBadge:badge];
6265 [buttonbar_ setBadgeValue:nil forButton:3];
6266 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6267 [buttonbar_ setBadgeAnimated:NO forButton:3];
6268 [self removeApplicationBadge];
6274 if ([packages count] == 0);
6286 [hud removeFromSuperview];*/
6289 - (void) _saveConfig {
6292 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6298 - (void) updateData {
6301 /* XXX: this is just stupid */
6302 if (tag_ != 2 && sections_ != nil)
6303 [sections_ reloadData];
6304 if (tag_ != 3 && changes_ != nil)
6305 [changes_ reloadData];
6306 if (tag_ != 5 && search_ != nil)
6307 [search_ reloadData];
6317 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6318 _assert(file != NULL);
6320 NSArray *keys = [Sources_ allKeys];
6322 for (int i(0), e([keys count]); i != e; ++i) {
6323 NSString *key = [keys objectAtIndex:i];
6324 NSDictionary *source = [Sources_ objectForKey:key];
6326 fprintf(file, "%s %s %s\n",
6327 [[source objectForKey:@"Type"] UTF8String],
6328 [[source objectForKey:@"URI"] UTF8String],
6329 [[source objectForKey:@"Distribution"] UTF8String]
6338 detachNewThreadSelector:@selector(update_)
6341 title:@"Updating Sources"
6345 - (void) reloadData {
6346 @synchronized (self) {
6347 if (confirm_ == nil)
6353 pkgProblemResolver *resolver = [database_ resolver];
6355 resolver->InstallProtect();
6356 if (!resolver->Resolve(true))
6361 [database_ prepare];
6363 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6364 [confirm_ setDelegate:self];
6366 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6367 [page setDelegate:self];
6369 [confirm_ setPage:page];
6370 [underlay_ popSubview:confirm_];
6373 - (void) installPackage:(Package *)package {
6374 @synchronized (self) {
6381 - (void) removePackage:(Package *)package {
6382 @synchronized (self) {
6389 - (void) distUpgrade {
6390 @synchronized (self) {
6391 [database_ upgrade];
6397 @synchronized (self) {
6399 if (confirm_ != nil) {
6407 [overlay_ removeFromSuperview];
6411 detachNewThreadSelector:@selector(perform)
6418 - (void) bootstrap_ {
6420 [database_ upgrade];
6421 [database_ prepare];
6422 [database_ perform];
6425 - (void) bootstrap {
6427 detachNewThreadSelector:@selector(bootstrap_)
6430 title:@"Bootstrap Install"
6434 - (void) progressViewIsComplete:(ProgressView *)progress {
6435 if (confirm_ != nil) {
6436 [underlay_ addSubview:overlay_];
6437 [confirm_ popFromSuperviewAnimated:NO];
6443 - (void) setPage:(RVPage *)page {
6444 [page resetViewAnimated:NO];
6445 [page setDelegate:self];
6446 [book_ setPage:page];
6449 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6450 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6451 [browser loadURL:url];
6455 - (void) _setHomePage {
6456 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6459 - (void) buttonBarItemTapped:(id)sender {
6460 unsigned tag = [sender tag];
6462 [book_ resetViewAnimated:YES];
6464 } else if (tag_ == 2 && tag != 2)
6465 [sections_ resetView];
6468 case 1: [self _setHomePage]; break;
6470 case 2: [self setPage:sections_]; break;
6471 case 3: [self setPage:changes_]; break;
6472 case 4: [self setPage:manage_]; break;
6473 case 5: [self setPage:search_]; break;
6475 default: _assert(false);
6481 - (void) applicationWillSuspend {
6483 [super applicationWillSuspend];
6486 - (void) askForSettings {
6487 UIActionSheet *role = [[[UIActionSheet alloc]
6488 initWithTitle:@"Who Are You?"
6489 buttons:[NSArray arrayWithObjects:
6490 @"User (Graphical Only)",
6491 @"Hacker (+ Command Line)",
6492 @"Developer (No Filters)",
6494 defaultButtonIndex:-1
6499 [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."];
6500 [role popupAlertAnimated:YES];
6505 [self setStatusBarShowsProgress:NO];
6508 [hud_ removeFromSuperview];
6512 pid_t pid = ExecFork();
6514 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6515 perror("launchctl stop");
6522 [self askForSettings];
6526 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6528 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6529 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6530 0, 0, screenrect.size.width, screenrect.size.height - 48
6531 ) database:database_];
6533 [book_ setDelegate:self];
6535 [overlay_ addSubview:book_];
6537 NSArray *buttonitems = [NSArray arrayWithObjects:
6538 [NSDictionary dictionaryWithObjectsAndKeys:
6539 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6540 @"home-up.png", kUIButtonBarButtonInfo,
6541 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6542 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6543 self, kUIButtonBarButtonTarget,
6544 @"Home", kUIButtonBarButtonTitle,
6545 @"0", kUIButtonBarButtonType,
6548 [NSDictionary dictionaryWithObjectsAndKeys:
6549 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6550 @"install-up.png", kUIButtonBarButtonInfo,
6551 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6552 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6553 self, kUIButtonBarButtonTarget,
6554 @"Sections", kUIButtonBarButtonTitle,
6555 @"0", kUIButtonBarButtonType,
6558 [NSDictionary dictionaryWithObjectsAndKeys:
6559 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6560 @"changes-up.png", kUIButtonBarButtonInfo,
6561 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6562 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6563 self, kUIButtonBarButtonTarget,
6564 @"Changes", kUIButtonBarButtonTitle,
6565 @"0", kUIButtonBarButtonType,
6568 [NSDictionary dictionaryWithObjectsAndKeys:
6569 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6570 @"manage-up.png", kUIButtonBarButtonInfo,
6571 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6572 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6573 self, kUIButtonBarButtonTarget,
6574 @"Manage", kUIButtonBarButtonTitle,
6575 @"0", kUIButtonBarButtonType,
6578 [NSDictionary dictionaryWithObjectsAndKeys:
6579 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6580 @"search-up.png", kUIButtonBarButtonInfo,
6581 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6582 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6583 self, kUIButtonBarButtonTarget,
6584 @"Search", kUIButtonBarButtonTitle,
6585 @"0", kUIButtonBarButtonType,
6589 buttonbar_ = [[UIToolbar alloc]
6591 withFrame:CGRectMake(
6592 0, screenrect.size.height - ButtonBarHeight_,
6593 screenrect.size.width, ButtonBarHeight_
6595 withItemList:buttonitems
6598 [buttonbar_ setDelegate:self];
6599 [buttonbar_ setBarStyle:1];
6600 [buttonbar_ setButtonBarTrackingMode:2];
6602 int buttons[5] = {1, 2, 3, 4, 5};
6603 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6604 [buttonbar_ showButtonGroup:0 withDuration:0];
6606 for (int i = 0; i != 5; ++i)
6607 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6608 i * 64 + 2, 1, 60, ButtonBarHeight_
6611 [buttonbar_ showSelectionForButton:1];
6612 [overlay_ addSubview:buttonbar_];
6614 [UIKeyboard initImplementationNow];
6615 CGSize keysize = [UIKeyboard defaultSize];
6616 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6617 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6618 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6619 [overlay_ addSubview:keyboard_];
6622 [underlay_ addSubview:overlay_];
6626 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6627 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6628 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6630 manage_ = (ManageView *) [[self
6631 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6632 withClass:[ManageView class]
6638 [self _setHomePage];
6641 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6642 NSString *context([sheet context]);
6644 if ([context isEqualToString:@"missing"])
6646 else if ([context isEqualToString:@"fixhalf"]) {
6649 @synchronized (self) {
6650 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6651 Package *broken = [broken_ objectAtIndex:i];
6654 NSString *id = [broken id];
6655 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6656 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6657 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6658 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6667 [broken_ removeAllObjects];
6676 } else if ([context isEqualToString:@"role"]) {
6678 case 1: Role_ = @"User"; break;
6679 case 2: Role_ = @"Hacker"; break;
6680 case 3: Role_ = @"Developer"; break;
6687 bool reset = Settings_ != nil;
6689 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6693 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6703 } else if ([context isEqualToString:@"upgrade"]) {
6706 @synchronized (self) {
6707 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6708 Package *essential = [essential_ objectAtIndex:i];
6709 [essential install];
6733 - (void) reorganize { _pooled
6734 system("/usr/libexec/cydia/free.sh");
6735 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6738 - (void) applicationSuspend:(__GSEvent *)event {
6739 if (hud_ == nil && ![progress_ isRunning])
6740 [super applicationSuspend:event];
6743 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6745 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6748 - (void) _setSuspended:(BOOL)value {
6750 [super _setSuspended:value];
6753 - (UIProgressHUD *) addProgressHUD {
6754 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6756 [underlay_ addSubview:hud];
6760 - (void) openMailToURL:(NSURL *)url {
6761 // XXX: this makes me sad
6763 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6765 [UIApp openURL:url];// asPanel:YES];
6769 - (void) clearFirstResponder {
6770 if (id responder = [window_ firstResponder])
6771 [responder resignFirstResponder];
6774 - (RVPage *) pageForPackage:(NSString *)name {
6775 if (Package *package = [database_ packageWithName:name]) {
6776 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6777 [view setPackage:package];
6780 UIActionSheet *sheet = [[[UIActionSheet alloc]
6781 initWithTitle:@"Cannot Locate Package"
6782 buttons:[NSArray arrayWithObjects:@"Close", nil]
6783 defaultButtonIndex:0
6788 [sheet setBodyText:[NSString stringWithFormat:
6789 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6792 [sheet popupAlertAnimated:YES];
6797 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6801 NSString *scheme([[url scheme] lowercaseString]);
6802 if (![scheme isEqualToString:@"cydia"])
6804 NSString *path([url absoluteString]);
6805 if ([path length] < 8)
6807 path = [path substringFromIndex:8];
6808 if (![path hasPrefix:@"/"])
6809 path = [@"/" stringByAppendingString:path];
6811 if ([path isEqualToString:@"/add-source"])
6812 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6813 else if ([path isEqualToString:@"/storage"])
6814 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6815 else if ([path isEqualToString:@"/sources"])
6816 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6817 else if ([path isEqualToString:@"/packages"])
6818 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6819 else if ([path hasPrefix:@"/url/"])
6820 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
6821 else if ([path hasPrefix:@"/launch/"])
6822 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
6823 else if ([path hasPrefix:@"/package-settings/"])
6824 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
6825 else if ([path hasPrefix:@"/package-signature/"])
6826 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
6827 else if ([path hasPrefix:@"/package/"])
6828 return [self pageForPackage:[path substringFromIndex:9]];
6829 else if ([path hasPrefix:@"/files/"]) {
6830 NSString *name = [path substringFromIndex:7];
6832 if (Package *package = [database_ packageWithName:name]) {
6833 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6834 [files setPackage:package];
6842 - (void) applicationOpenURL:(NSURL *)url {
6843 [super applicationOpenURL:url];
6845 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6846 [self setPage:page];
6847 [buttonbar_ showSelectionForButton:tag];
6852 - (void) applicationDidFinishLaunching:(id)unused {
6853 Font12_ = [[UIFont systemFontOfSize:12] retain];
6854 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6855 Font14_ = [[UIFont systemFontOfSize:14] retain];
6856 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6857 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6859 _assert(pkgInitConfig(*_config));
6860 _assert(pkgInitSystem(*_config, _system));
6864 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6865 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6867 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6869 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6870 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6872 [window_ orderFront:self];
6873 [window_ makeKey:self];
6874 [window_ setHidden:NO];
6876 database_ = [Database sharedInstance];
6877 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6878 [database_ setDelegate:progress_];
6879 [window_ setContentView:progress_];
6881 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6882 [progress_ setContentView:underlay_];
6884 [progress_ resetView];
6887 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6888 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6889 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6890 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6891 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6892 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6893 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6895 [self setIdleTimerDisabled:YES];
6897 hud_ = [self addProgressHUD];
6898 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6900 [self setStatusBarShowsProgress:YES];
6903 detachNewThreadSelector:@selector(reorganize)
6911 - (void) showKeyboard:(BOOL)show {
6912 CGSize keysize = [UIKeyboard defaultSize];
6913 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6914 CGRect keyup = keydown;
6915 keyup.origin.y -= keysize.height;
6917 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6918 [animation setSignificantRectFields:2];
6921 [animation setStartFrame:keydown];
6922 [animation setEndFrame:keyup];
6923 [keyboard_ activate];
6925 [animation setStartFrame:keyup];
6926 [animation setEndFrame:keydown];
6927 [keyboard_ deactivate];
6930 [[UIAnimator sharedAnimator]
6931 addAnimations:[NSArray arrayWithObjects:animation, nil]
6932 withDuration:KeyboardTime_
6937 - (void) slideUp:(UIActionSheet *)alert {
6939 [alert presentSheetFromButtonBar:buttonbar_];
6941 [alert presentSheetInView:overlay_];
6946 void AddPreferences(NSString *plist) { _pooled
6947 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6948 _assert(settings != NULL);
6949 NSMutableArray *items = [settings objectForKey:@"items"];
6953 for (size_t i(0); i != [items count]; ++i) {
6954 NSMutableDictionary *item([items objectAtIndex:i]);
6955 NSString *label = [item objectForKey:@"label"];
6956 if (label != nil && [label isEqualToString:@"Cydia"]) {
6963 for (size_t i(0); i != [items count]; ++i) {
6964 NSDictionary *item([items objectAtIndex:i]);
6965 NSString *label = [item objectForKey:@"label"];
6966 if (label != nil && [label isEqualToString:@"General"]) {
6967 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6968 @"CydiaSettings", @"bundle",
6969 @"PSLinkCell", @"cell",
6970 [NSNumber numberWithBool:YES], @"hasIcon",
6971 [NSNumber numberWithBool:YES], @"isController",
6973 nil] atIndex:(i + 1)];
6979 _assert([settings writeToFile:plist atomically:YES] == YES);
6984 id Alloc_(id self, SEL selector) {
6985 id object = alloc_(self, selector);
6986 lprintf("[%s]A-%p\n", self->isa->name, object);
6991 id Dealloc_(id self, SEL selector) {
6992 id object = dealloc_(self, selector);
6993 lprintf("[%s]D-%p\n", self->isa->name, object);
6997 int main(int argc, char *argv[]) { _pooled
6998 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7000 bool substrate(false);
7006 for (int argi(1); argi != argc; ++argi)
7007 if (strcmp(argv[argi], "--") == 0) {
7009 argv[argi] = argv[0];
7015 for (int argi(1); argi != arge; ++argi)
7016 if (strcmp(args[argi], "--bootstrap") == 0)
7018 else if (strcmp(args[argi], "--substrate") == 0)
7021 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7024 App_ = [[NSBundle mainBundle] bundlePath];
7025 Home_ = NSHomeDirectory();
7026 Locale_ = CFLocaleCopyCurrent();
7029 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7030 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7031 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7032 Sounds_Keyboard_ = [keyboard boolValue];
7038 #if 1 /* XXX: this costs 1.4s of startup performance */
7039 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7040 _assert(errno == ENOENT);
7041 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7042 _assert(errno == ENOENT);
7045 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7046 alloc_ = alloc->method_imp;
7047 alloc->method_imp = (IMP) &Alloc_;*/
7049 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7050 dealloc_ = dealloc->method_imp;
7051 dealloc->method_imp = (IMP) &Dealloc_;*/
7056 size = sizeof(maxproc);
7057 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7058 perror("sysctlbyname(\"kern.maxproc\", ?)");
7059 else if (maxproc < 64) {
7061 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7062 perror("sysctlbyname(\"kern.maxproc\", #)");
7065 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7066 char *machine = new char[size];
7067 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7068 perror("sysctlbyname(\"hw.machine\", ?)");
7072 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7074 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7075 Build_ = [system objectForKey:@"ProductBuildVersion"];
7077 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7078 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7080 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7081 Indices_ = [[NSMutableDictionary alloc] init];*/
7083 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7084 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7085 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7088 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7089 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7091 Settings_ = [Metadata_ objectForKey:@"Settings"];
7093 Packages_ = [Metadata_ objectForKey:@"Packages"];
7094 Sections_ = [Metadata_ objectForKey:@"Sections"];
7095 Sources_ = [Metadata_ objectForKey:@"Sources"];
7098 if (Settings_ != nil)
7099 Role_ = [Settings_ objectForKey:@"Role"];
7101 if (Packages_ == nil) {
7102 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7103 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7106 if (Sections_ == nil) {
7107 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7108 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7111 if (Sources_ == nil) {
7112 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7113 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7117 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7120 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7121 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7122 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7123 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7125 if (access("/User", F_OK) != 0)
7126 system("/usr/libexec/cydia/firmware.sh");
7128 _assert([[NSFileManager defaultManager]
7129 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7130 withIntermediateDirectories:YES
7135 space_ = CGColorSpaceCreateDeviceRGB();
7137 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7138 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7139 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7140 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7141 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7142 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7144 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7146 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7148 UIApplicationUseLegacyEvents(YES);
7149 UIKeyboardDisableAutomaticAppearance();
7151 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7153 CGColorSpaceRelease(space_);