1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #import "UICaboodle.h"
41 #include <objc/objc.h>
42 #include <objc/runtime.h>
44 #include <CoreGraphics/CoreGraphics.h>
45 #include <GraphicsServices/GraphicsServices.h>
46 #include <Foundation/Foundation.h>
48 #import <QuartzCore/CALayer.h>
50 #import <UIKit/UIKit.h>
53 #import <MessageUI/MailComposeController.h>
58 #include <ext/stdio_filebuf.h>
60 #include <apt-pkg/acquire.h>
61 #include <apt-pkg/acquire-item.h>
62 #include <apt-pkg/algorithms.h>
63 #include <apt-pkg/cachefile.h>
64 #include <apt-pkg/clean.h>
65 #include <apt-pkg/configuration.h>
66 #include <apt-pkg/debmetaindex.h>
67 #include <apt-pkg/error.h>
68 #include <apt-pkg/init.h>
69 #include <apt-pkg/mmap.h>
70 #include <apt-pkg/pkgrecords.h>
71 #include <apt-pkg/sha1.h>
72 #include <apt-pkg/sourcelist.h>
73 #include <apt-pkg/sptr.h>
74 #include <apt-pkg/strutl.h>
76 #include <sys/types.h>
78 #include <sys/sysctl.h>
84 #include <mach-o/nlist.h>
94 #import "BrowserView.h"
100 //#define _finline __attribute__((force_inline))
101 #define _finline inline
106 #define _limit(count) do { \
107 static size_t _count(0); \
108 if (++_count == count) \
112 static uint64_t profile_;
114 #define _timestamp ({ \
116 gettimeofday(&tv, NULL); \
117 tv.tv_sec * 1000000 + tv.tv_usec; \
120 /* Objective-C Handle<> {{{ */
121 template <typename Type_>
123 typedef _H<Type_> This_;
128 _finline void Retain_() {
133 _finline void Clear_() {
139 _finline _H(Type_ *value = NULL, bool mended = false) :
150 _finline This_ &operator =(Type_ *value) {
151 if (value_ != value) {
160 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
162 void NSLogPoint(const char *fix, const CGPoint &point) {
163 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
166 void NSLogRect(const char *fix, const CGRect &rect) {
167 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
170 /* NSForcedOrderingSearch doesn't work on the iPhone */
171 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
172 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
173 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
175 /* iPhoneOS 2.0 Compatibility {{{ */
177 @interface UITextView (iPhoneOS)
178 - (void) setTextSize:(float)size;
181 @implementation UITextView (iPhoneOS)
183 - (void) setTextSize:(float)size {
184 [self setFont:[[self font] fontWithSize:size]];
191 extern NSString * const kCAFilterNearest;
193 /* Information Dictionaries {{{ */
194 @interface NSMutableArray (Cydia)
195 - (void) addInfoDictionary:(NSDictionary *)info;
198 @implementation NSMutableArray (Cydia)
200 - (void) addInfoDictionary:(NSDictionary *)info {
201 [self addObject:info];
206 @interface NSMutableDictionary (Cydia)
207 - (void) addInfoDictionary:(NSDictionary *)info;
210 @implementation NSMutableDictionary (Cydia)
212 - (void) addInfoDictionary:(NSDictionary *)info {
213 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
214 [self setObject:info forKey:bundle];
219 /* Pop Transitions {{{ */
220 @interface PopTransitionView : UITransitionView {
225 @implementation PopTransitionView
227 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
228 if (from != nil && to == nil)
229 [self removeFromSuperview];
234 @interface UIView (PopUpView)
235 - (void) popFromSuperviewAnimated:(BOOL)animated;
236 - (void) popSubview:(UIView *)view;
239 @implementation UIView (PopUpView)
241 - (void) popFromSuperviewAnimated:(BOOL)animated {
242 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
245 - (void) popSubview:(UIView *)view {
246 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
247 [transition setDelegate:transition];
248 [self addSubview:transition];
250 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
251 [transition transition:UITransitionNone toView:blank];
252 [transition transition:UITransitionPushFromBottom toView:view];
258 #define lprintf(args...) fprintf(stderr, args)
261 #define ForSaurik (1 && !ForRelease)
262 #define RecycleWebViews 0
263 #define AlwaysReload (1 && !ForRelease)
266 @interface NSMutableArray (Radix)
267 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
270 @implementation NSMutableArray (Radix)
272 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
273 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
274 [invocation setSelector:selector];
275 [invocation setArgument:&object atIndex:2];
277 size_t count([self count]);
282 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
284 for (size_t i(0); i != count; ++i) {
285 RadixItem &item(lhs[i]);
288 id object([self objectAtIndex:i]);
289 [invocation setTarget:object];
292 [invocation getReturnValue:&item.key];
295 static const size_t width = 32;
296 static const size_t bits = 11;
297 static const size_t slots = 1 << bits;
298 static const size_t passes = (width + (bits - 1)) / bits;
300 size_t *hist(new size_t[slots]);
302 for (size_t pass(0); pass != passes; ++pass) {
303 memset(hist, 0, sizeof(size_t) * slots);
305 for (size_t i(0); i != count; ++i) {
306 uint32_t key(lhs[i].key);
308 key &= _not(uint32_t) >> width - bits;
313 for (size_t i(0); i != slots; ++i) {
314 size_t local(offset);
319 for (size_t i(0); i != count; ++i) {
320 uint32_t key(lhs[i].key);
322 key &= _not(uint32_t) >> width - bits;
323 rhs[hist[key]++] = lhs[i];
333 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
334 for (size_t i(0); i != count; ++i)
335 [values addObject:[self objectAtIndex:lhs[i].index]];
336 [self setArray:values];
344 /* Apple Bug Fixes {{{ */
345 @implementation UIWebDocumentView (Cydia)
347 - (void) _setScrollerOffset:(CGPoint)offset {
348 UIScroller *scroller([self _scroller]);
350 CGSize size([scroller contentSize]);
351 CGSize bounds([scroller bounds].size);
354 max.x = size.width - bounds.width;
355 max.y = size.height - bounds.height;
363 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
364 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
366 [scroller setOffset:offset];
373 kUIControlEventMouseDown = 1 << 0,
374 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
375 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
376 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
377 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
378 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
379 } UIControlEventMasks;
381 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
382 size_t length([self length] - state->state);
385 else if (length > count)
387 for (size_t i(0); i != length; ++i)
388 objects[i] = [self item:state->state++];
389 state->itemsPtr = objects;
390 state->mutationsPtr = (unsigned long *) self;
394 @interface NSString (UIKit)
395 - (NSString *) stringByAddingPercentEscapes;
396 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
399 @interface NSString (Cydia)
400 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
401 - (NSComparisonResult) compareByPath:(NSString *)other;
404 @implementation NSString (Cydia)
406 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
407 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
410 - (NSComparisonResult) compareByPath:(NSString *)other {
411 NSString *prefix = [self commonPrefixWithString:other options:0];
412 size_t length = [prefix length];
414 NSRange lrange = NSMakeRange(length, [self length] - length);
415 NSRange rrange = NSMakeRange(length, [other length] - length);
417 lrange = [self rangeOfString:@"/" options:0 range:lrange];
418 rrange = [other rangeOfString:@"/" options:0 range:rrange];
420 NSComparisonResult value;
422 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
423 value = NSOrderedSame;
424 else if (lrange.location == NSNotFound)
425 value = NSOrderedAscending;
426 else if (rrange.location == NSNotFound)
427 value = NSOrderedDescending;
429 value = NSOrderedSame;
431 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
432 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
433 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
434 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
436 NSComparisonResult result = [lpath compare:rpath];
437 return result == NSOrderedSame ? value : result;
442 /* Perl-Compatible RegEx {{{ */
452 Pcre(const char *regex) :
457 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
460 lprintf("%d:%s\n", offset, error);
464 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
465 matches_ = new int[(capture_ + 1) * 3];
473 NSString *operator [](size_t match) {
474 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
477 bool operator ()(NSString *data) {
478 // XXX: length is for characters, not for bytes
479 return operator ()([data UTF8String], [data length]);
482 bool operator ()(const char *data, size_t size) {
484 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
488 /* Mime Addresses {{{ */
489 @interface Address : NSObject {
495 - (NSString *) address;
497 + (Address *) addressWithString:(NSString *)string;
498 - (Address *) initWithString:(NSString *)string;
501 @implementation Address
510 - (NSString *) name {
514 - (NSString *) address {
518 + (Address *) addressWithString:(NSString *)string {
519 return [[[Address alloc] initWithString:string] autorelease];
522 + (NSArray *) _attributeKeys {
523 return [NSArray arrayWithObjects:@"address", @"name", nil];
526 - (NSArray *) attributeKeys {
527 return [[self class] _attributeKeys];
530 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
531 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
534 - (Address *) initWithString:(NSString *)string {
535 if ((self = [super init]) != nil) {
536 const char *data = [string UTF8String];
537 size_t size = [string length];
539 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
541 if (address_r(data, size)) {
542 name_ = [address_r[1] retain];
543 address_ = [address_r[2] retain];
545 name_ = [string retain];
553 /* CoreGraphics Primitives {{{ */
564 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
567 Set(space, red, green, blue, alpha);
572 CGColorRelease(color_);
579 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
581 float color[] = {red, green, blue, alpha};
582 color_ = CGColorCreate(space, color);
585 operator CGColorRef() {
591 extern "C" void UISetColor(CGColorRef color);
593 /* Random Global Variables {{{ */
594 static const int PulseInterval_ = 50000;
595 static const int ButtonBarHeight_ = 48;
596 static const float KeyboardTime_ = 0.3f;
598 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
599 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
600 #define NotifyConfig_ "/etc/notify.conf"
602 static CGColor Blue_;
603 static CGColor Blueish_;
604 static CGColor Black_;
606 static CGColor White_;
607 static CGColor Gray_;
609 static NSString *App_;
610 static NSString *Home_;
611 static BOOL Sounds_Keyboard_;
613 static BOOL Advanced_;
617 static BOOL Ignored_;
619 static UIFont *Font12_;
620 static UIFont *Font12Bold_;
621 static UIFont *Font14_;
622 static UIFont *Font18Bold_;
623 static UIFont *Font22Bold_;
625 static const char *Machine_ = NULL;
626 static const NSString *UniqueID_ = NULL;
633 CGColorSpaceRef space_;
635 #define FW_LEAST(major, minor, bugfix) \
636 (major < Major_ || major == Major_ && \
637 (minor < Minor_ || minor == Minor_ && \
643 static NSDictionary *SectionMap_;
644 static NSMutableDictionary *Metadata_;
645 static NSMutableDictionary *Indices_;
646 static _transient NSMutableDictionary *Settings_;
647 static _transient NSString *Role_;
648 static _transient NSMutableDictionary *Packages_;
649 static _transient NSMutableDictionary *Sections_;
650 static _transient NSMutableDictionary *Sources_;
651 static bool Changed_;
655 static NSMutableArray *Documents_;
658 NSString *GetLastUpdate() {
659 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
662 return @"Never or Unknown";
664 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
665 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
667 CFRelease(formatter);
669 return [(NSString *) formatted autorelease];
672 /* Display Helpers {{{ */
673 inline float Interpolate(float begin, float end, float fraction) {
674 return (end - begin) * fraction + begin;
677 NSString *SizeString(double size) {
678 bool negative = size < 0;
683 while (size > 1024) {
688 static const char *powers_[] = {"B", "kB", "MB", "GB"};
690 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
693 NSString *StripVersion(NSString *version) {
694 NSRange colon = [version rangeOfString:@":"];
695 if (colon.location != NSNotFound)
696 version = [version substringFromIndex:(colon.location + 1)];
700 NSString *Simplify(NSString *title) {
701 const char *data = [title UTF8String];
702 size_t size = [title length];
704 static Pcre square_r("^\\[(.*)\\]$");
705 if (square_r(data, size))
706 return Simplify(square_r[1]);
708 static Pcre paren_r("^\\((.*)\\)$");
709 if (paren_r(data, size))
710 return Simplify(paren_r[1]);
712 static Pcre title_r("^(.*?) \\(.*\\)$");
713 if (title_r(data, size))
714 return Simplify(title_r[1]);
720 bool isSectionVisible(NSString *section) {
721 NSDictionary *metadata = [Sections_ objectForKey:section];
722 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
723 return hidden == nil || ![hidden boolValue];
726 /* Delegate Prototypes {{{ */
730 @interface NSObject (ProgressDelegate)
733 @implementation NSObject(ProgressDelegate)
735 - (void) _setProgressError:(NSArray *)args {
736 [self performSelector:@selector(setProgressError:forPackage:)
737 withObject:[args objectAtIndex:0]
738 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
744 @protocol ProgressDelegate
745 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
746 - (void) setProgressTitle:(NSString *)title;
747 - (void) setProgressPercent:(float)percent;
748 - (void) startProgress;
749 - (void) addProgressOutput:(NSString *)output;
750 - (bool) isCancelling:(size_t)received;
753 @protocol ConfigurationDelegate
754 - (void) repairWithSelector:(SEL)selector;
755 - (void) setConfigurationData:(NSString *)data;
758 @protocol CydiaDelegate
759 - (void) installPackage:(Package *)package;
760 - (void) removePackage:(Package *)package;
761 - (void) slideUp:(UIActionSheet *)alert;
762 - (void) distUpgrade;
765 - (void) askForSettings;
766 - (UIProgressHUD *) addProgressHUD;
767 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
768 - (RVPage *) pageForPackage:(NSString *)name;
769 - (void) openMailToURL:(NSURL *)url;
770 - (void) clearFirstResponder;
774 /* Status Delegation {{{ */
776 public pkgAcquireStatus
779 _transient NSObject<ProgressDelegate> *delegate_;
787 void setDelegate(id delegate) {
788 delegate_ = delegate;
791 virtual bool MediaChange(std::string media, std::string drive) {
795 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
798 virtual void Fetch(pkgAcquire::ItemDesc &item) {
799 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
800 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
803 virtual void Done(pkgAcquire::ItemDesc &item) {
806 virtual void Fail(pkgAcquire::ItemDesc &item) {
808 item.Owner->Status == pkgAcquire::Item::StatIdle ||
809 item.Owner->Status == pkgAcquire::Item::StatDone
813 std::string &error(item.Owner->ErrorText);
817 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
818 NSArray *fields([description componentsSeparatedByString:@" "]);
819 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
821 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
822 withObject:[NSArray arrayWithObjects:
823 [NSString stringWithUTF8String:error.c_str()],
830 virtual bool Pulse(pkgAcquire *Owner) {
831 bool value = pkgAcquireStatus::Pulse(Owner);
834 double(CurrentBytes + CurrentItems) /
835 double(TotalBytes + TotalItems)
838 [delegate_ setProgressPercent:percent];
839 return [delegate_ isCancelling:CurrentBytes] ? false : value;
842 virtual void Start() {
843 [delegate_ startProgress];
846 virtual void Stop() {
850 /* Progress Delegation {{{ */
855 _transient id<ProgressDelegate> delegate_;
858 virtual void Update() {
859 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
860 [delegate_ setProgressPercent:(Percent / 100)];
869 void setDelegate(id delegate) {
870 delegate_ = delegate;
873 virtual void Done() {
874 [delegate_ setProgressPercent:1];
879 /* Database Interface {{{ */
880 @interface Database : NSObject {
882 pkgDepCache::Policy *policy_;
883 pkgRecords *records_;
884 pkgProblemResolver *resolver_;
885 pkgAcquire *fetcher_;
887 SPtr<pkgPackageManager> manager_;
888 pkgSourceList *list_;
890 NSMutableDictionary *sources_;
891 NSMutableArray *packages_;
893 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
902 + (Database *) sharedInstance;
904 - (void) _readCydia:(NSNumber *)fd;
905 - (void) _readStatus:(NSNumber *)fd;
906 - (void) _readOutput:(NSNumber *)fd;
910 - (Package *) packageWithName:(NSString *)name;
912 - (pkgCacheFile &) cache;
913 - (pkgDepCache::Policy *) policy;
914 - (pkgRecords *) records;
915 - (pkgProblemResolver *) resolver;
916 - (pkgAcquire &) fetcher;
917 - (NSArray *) packages;
918 - (NSArray *) sources;
927 - (void) updateWithStatus:(Status &)status;
929 - (void) setDelegate:(id)delegate;
930 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
934 /* Source Class {{{ */
935 @interface Source : NSObject {
936 NSString *description_;
941 NSString *distribution_;
945 NSString *defaultIcon_;
947 NSDictionary *record_;
951 - (Source *) initWithMetaIndex:(metaIndex *)index;
953 - (NSComparisonResult) compareByNameAndType:(Source *)source;
955 - (NSDictionary *) record;
959 - (NSString *) distribution;
965 - (NSString *) description;
966 - (NSString *) label;
967 - (NSString *) origin;
968 - (NSString *) version;
970 - (NSString *) defaultIcon;
974 @implementation Source
978 [distribution_ release];
981 if (description_ != nil)
982 [description_ release];
989 if (defaultIcon_ != nil)
990 [defaultIcon_ release];
997 + (NSArray *) _attributeKeys {
998 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1001 - (NSArray *) attributeKeys {
1002 return [[self class] _attributeKeys];
1005 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1006 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1009 - (Source *) initWithMetaIndex:(metaIndex *)index {
1010 if ((self = [super init]) != nil) {
1011 trusted_ = index->IsTrusted();
1013 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1014 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1015 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
1017 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1018 if (dindex != NULL) {
1019 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1021 while (std::getline(release, line)) {
1022 std::string::size_type colon(line.find(':'));
1023 if (colon == std::string::npos)
1026 std::string name(line.substr(0, colon));
1027 std::string value(line.substr(colon + 1));
1028 while (!value.empty() && value[0] == ' ')
1029 value = value.substr(1);
1031 if (name == "Default-Icon")
1032 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1033 else if (name == "Description")
1034 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1035 else if (name == "Label")
1036 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1037 else if (name == "Origin")
1038 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1039 else if (name == "Version")
1040 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
1044 record_ = [Sources_ objectForKey:[self key]];
1046 record_ = [record_ retain];
1050 - (NSComparisonResult) compareByNameAndType:(Source *)source {
1051 NSDictionary *lhr = [self record];
1052 NSDictionary *rhr = [source record];
1055 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1057 NSString *lhs = [self name];
1058 NSString *rhs = [source name];
1060 if ([lhs length] != 0 && [rhs length] != 0) {
1061 unichar lhc = [lhs characterAtIndex:0];
1062 unichar rhc = [rhs characterAtIndex:0];
1064 if (isalpha(lhc) && !isalpha(rhc))
1065 return NSOrderedAscending;
1066 else if (!isalpha(lhc) && isalpha(rhc))
1067 return NSOrderedDescending;
1070 return [lhs compare:rhs options:LaxCompareOptions_];
1073 - (NSDictionary *) record {
1081 - (NSString *) uri {
1085 - (NSString *) distribution {
1086 return distribution_;
1089 - (NSString *) type {
1093 - (NSString *) key {
1094 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1097 - (NSString *) host {
1098 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1101 - (NSString *) name {
1102 return origin_ == nil ? [self host] : origin_;
1105 - (NSString *) description {
1106 return description_;
1109 - (NSString *) label {
1110 return label_ == nil ? [self host] : label_;
1113 - (NSString *) origin {
1117 - (NSString *) version {
1121 - (NSString *) defaultIcon {
1122 return defaultIcon_;
1127 /* Relationship Class {{{ */
1128 @interface Relationship : NSObject {
1133 - (NSString *) type;
1135 - (NSString *) name;
1139 @implementation Relationship
1147 - (NSString *) type {
1155 - (NSString *) name {
1162 /* Package Class {{{ */
1163 @interface Package : NSObject {
1164 pkgCache::PkgIterator iterator_;
1165 _transient Database *database_;
1166 pkgCache::VerIterator version_;
1167 pkgCache::VerFileIterator file_;
1175 NSString *installed_;
1181 NSString *depiction_;
1182 NSString *homepage_;
1188 NSArray *relationships_;
1191 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1192 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1194 - (pkgCache::PkgIterator) iterator;
1196 - (NSString *) section;
1197 - (NSString *) simpleSection;
1199 - (Address *) maintainer;
1201 - (NSString *) description;
1202 - (NSString *) index;
1204 - (NSMutableDictionary *) metadata;
1206 - (BOOL) subscribed;
1209 - (NSString *) latest;
1210 - (NSString *) installed;
1213 - (BOOL) upgradableAndEssential:(BOOL)essential;
1216 - (BOOL) unfiltered;
1220 - (BOOL) halfConfigured;
1221 - (BOOL) halfInstalled;
1223 - (NSString *) mode;
1226 - (NSString *) name;
1227 - (NSString *) tagline;
1229 - (NSString *) homepage;
1230 - (NSString *) depiction;
1231 - (Address *) author;
1233 - (NSArray *) files;
1234 - (NSArray *) relationships;
1235 - (NSArray *) warnings;
1236 - (NSArray *) applications;
1238 - (Source *) source;
1239 - (NSString *) role;
1240 - (NSString *) rating;
1242 - (BOOL) matches:(NSString *)text;
1244 - (bool) hasSupportingRole;
1245 - (BOOL) hasTag:(NSString *)tag;
1246 - (NSString *) primaryPurpose;
1247 - (NSArray *) purposes;
1249 - (NSComparisonResult) compareByName:(Package *)package;
1250 - (NSComparisonResult) compareBySection:(Package *)package;
1252 - (uint32_t) compareForChanges;
1257 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1258 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1259 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1260 - (NSNumber *) isVisibleInSource:(Source *)source;
1264 @implementation Package
1270 if (section_ != nil)
1274 if (installed_ != nil)
1275 [installed_ release];
1283 if (depiction_ != nil)
1284 [depiction_ release];
1285 if (homepage_ != nil)
1286 [homepage_ release];
1287 if (sponsor_ != nil)
1296 if (relationships_ != nil)
1297 [relationships_ release];
1302 + (NSArray *) _attributeKeys {
1303 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
1306 - (NSArray *) attributeKeys {
1307 return [[self class] _attributeKeys];
1310 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1311 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1314 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1315 if ((self = [super init]) != nil) {
1316 iterator_ = iterator;
1317 database_ = database;
1319 version_ = [database_ policy]->GetCandidateVer(iterator_);
1320 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1321 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
1323 pkgCache::VerIterator current = iterator_.CurrentVer();
1324 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1325 installed_ = [StripVersion(installed) retain];
1327 if (!version_.end())
1328 file_ = version_.FileList();
1330 pkgCache &cache([database_ cache]);
1331 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1334 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
1337 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1339 const char *begin, *end;
1340 parser->GetRec(begin, end);
1342 NSString *website(nil);
1343 NSString *sponsor(nil);
1344 NSString *author(nil);
1353 {"depiction", &depiction_},
1354 {"homepage", &homepage_},
1355 {"website", &website},
1356 {"sponsor", &sponsor},
1357 {"author", &author},
1361 while (begin != end)
1362 if (*begin == '\n') {
1365 } else if (isblank(*begin)) next: {
1366 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1369 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1370 const char *name(begin);
1371 size_t size(colon - begin);
1373 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1376 const char *stop(begin == NULL ? end : begin);
1377 while (stop[-1] == '\r')
1379 while (++colon != stop && isblank(*colon));
1381 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1382 if (strncasecmp(names[i].name_, name, size) == 0) {
1383 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1384 *names[i].value_ = value;
1395 name_ = [name_ retain];
1396 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1398 icon_ = [icon_ retain];
1399 if (depiction_ != nil)
1400 depiction_ = [depiction_ retain];
1401 if (homepage_ == nil)
1402 homepage_ = website;
1403 if ([homepage_ isEqualToString:depiction_])
1405 if (homepage_ != nil)
1406 homepage_ = [homepage_ retain];
1408 sponsor_ = [[Address addressWithString:sponsor] retain];
1410 author_ = [[Address addressWithString:author] retain];
1412 tags_ = [[tag componentsSeparatedByString:@", "] retain];
1416 for (int i(0), e([tags_ count]); i != e; ++i) {
1417 NSString *tag = [tags_ objectAtIndex:i];
1418 if ([tag hasPrefix:@"role::"]) {
1419 role_ = [[tag substringFromIndex:6] retain];
1424 NSString *solid(latest == nil ? installed : latest);
1425 bool changed(false);
1427 NSString *key([id_ lowercaseString]);
1429 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
1430 if (metadata == nil) {
1431 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
1436 [metadata setObject:solid forKey:@"LastVersion"];
1439 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1440 NSDate *last([metadata objectForKey:@"LastSeen"]);
1441 NSString *version([metadata objectForKey:@"LastVersion"]);
1444 first = last == nil ? now_ : last;
1445 [metadata setObject:first forKey:@"FirstSeen"];
1450 if (version == nil) {
1451 [metadata setObject:solid forKey:@"LastVersion"];
1453 } else if (![version isEqualToString:solid]) {
1454 [metadata setObject:solid forKey:@"LastVersion"];
1456 [metadata setObject:last forKey:@"LastSeen"];
1462 [Packages_ setObject:metadata forKey:key];
1468 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1469 return [[[Package alloc]
1470 initWithIterator:iterator
1475 - (pkgCache::PkgIterator) iterator {
1479 - (NSString *) section {
1480 if (section_ != nil)
1483 const char *section = iterator_.Section();
1484 if (section == NULL)
1487 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1490 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1491 if (NSString *rename = [value objectForKey:@"Rename"]) {
1496 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1500 - (NSString *) simpleSection {
1501 if (NSString *section = [self section])
1502 return Simplify(section);
1508 - (Address *) maintainer {
1511 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1512 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1516 return version_.end() ? 0 : version_->InstalledSize;
1519 - (NSString *) description {
1522 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1523 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1525 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1526 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1527 if ([lines count] < 2)
1530 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1531 for (size_t i(1); i != [lines count]; ++i) {
1532 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1533 [trimmed addObject:trim];
1536 return [trimmed componentsJoinedByString:@"\n"];
1539 - (NSString *) index {
1540 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1541 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1544 - (NSMutableDictionary *) metadata {
1545 return [Packages_ objectForKey:[id_ lowercaseString]];
1549 NSDictionary *metadata([self metadata]);
1550 if ([self subscribed])
1551 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1553 return [metadata objectForKey:@"FirstSeen"];
1556 - (BOOL) subscribed {
1557 NSDictionary *metadata([self metadata]);
1558 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1559 return [subscribed boolValue];
1565 NSDictionary *metadata([self metadata]);
1566 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1567 return [ignored boolValue];
1572 - (NSString *) latest {
1576 - (NSString *) installed {
1581 return !version_.end();
1584 - (BOOL) upgradableAndEssential:(BOOL)essential {
1585 pkgCache::VerIterator current = iterator_.CurrentVer();
1588 return essential && [self essential];
1590 return !version_.end() && version_ != current;
1593 - (BOOL) essential {
1594 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1598 return [database_ cache][iterator_].InstBroken();
1601 - (BOOL) unfiltered {
1602 NSString *section = [self section];
1603 return section == nil || isSectionVisible(section);
1607 return [self hasSupportingRole] && [self unfiltered];
1611 unsigned char current = iterator_->CurrentState;
1612 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1615 - (BOOL) halfConfigured {
1616 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1619 - (BOOL) halfInstalled {
1620 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1624 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1625 return state.Mode != pkgDepCache::ModeKeep;
1628 - (NSString *) mode {
1629 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1631 switch (state.Mode) {
1632 case pkgDepCache::ModeDelete:
1633 if ((state.iFlags & pkgDepCache::Purge) != 0)
1637 case pkgDepCache::ModeKeep:
1638 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1642 case pkgDepCache::ModeInstall:
1643 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1644 return @"Reinstall";
1645 else switch (state.Status) {
1647 return @"Downgrade";
1653 return @"New Install";
1666 - (NSString *) name {
1667 return name_ == nil ? id_ : name_;
1670 - (NSString *) tagline {
1674 - (UIImage *) icon {
1675 NSString *section = [self simpleSection];
1678 if (NSString *icon = icon_)
1679 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1680 if (icon == nil) if (section != nil)
1681 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1682 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1683 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1685 icon = [UIImage applicationImageNamed:@"unknown.png"];
1689 - (NSString *) homepage {
1693 - (NSString *) depiction {
1697 - (Address *) sponsor {
1701 - (Address *) author {
1705 - (NSArray *) files {
1706 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1707 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1710 fin.open([path UTF8String]);
1715 while (std::getline(fin, line))
1716 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1721 - (NSArray *) relationships {
1722 return relationships_;
1725 - (NSArray *) warnings {
1726 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1727 const char *name(iterator_.Name());
1729 size_t length(strlen(name));
1730 if (length < 2) invalid:
1731 [warnings addObject:@"illegal package identifier"];
1732 else for (size_t i(0); i != length; ++i)
1734 (name[i] < 'a' || name[i] > 'z') &&
1735 (name[i] < '0' || name[i] > '9') &&
1736 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1739 if (strcmp(name, "cydia") != 0) {
1743 if (NSArray *files = [self files])
1744 for (NSString *file in files)
1745 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1747 else if (!stash && [file isEqualToString:@"/var/stash"])
1751 [warnings addObject:@"files installed into Cydia.app"];
1753 [warnings addObject:@"files installed to /var/stash"];
1756 return [warnings count] == 0 ? nil : warnings;
1759 - (NSArray *) applications {
1760 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1762 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1764 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1765 if (NSArray *files = [self files])
1766 for (NSString *file in files)
1767 if (application_r(file)) {
1768 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1769 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1770 if ([id isEqualToString:me])
1773 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1775 display = application_r[1];
1777 NSString *bundle([file stringByDeletingLastPathComponent]);
1778 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1779 if (icon == nil || [icon length] == 0)
1781 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1783 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1784 [applications addObject:application];
1786 [application addObject:id];
1787 [application addObject:display];
1788 [application addObject:url];
1791 return [applications count] == 0 ? nil : applications;
1794 - (Source *) source {
1796 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1803 - (NSString *) role {
1807 - (NSString *) rating {
1808 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1809 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1814 - (BOOL) matches:(NSString *)text {
1820 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1821 if (range.location != NSNotFound)
1824 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1825 if (range.location != NSNotFound)
1828 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1829 if (range.location != NSNotFound)
1835 - (bool) hasSupportingRole {
1838 if ([role_ isEqualToString:@"enduser"])
1840 if ([Role_ isEqualToString:@"User"])
1842 if ([role_ isEqualToString:@"hacker"])
1844 if ([Role_ isEqualToString:@"Hacker"])
1846 if ([role_ isEqualToString:@"developer"])
1848 if ([Role_ isEqualToString:@"Developer"])
1853 - (BOOL) hasTag:(NSString *)tag {
1854 return tags_ == nil ? NO : [tags_ containsObject:tag];
1857 - (NSString *) primaryPurpose {
1858 for (NSString *tag in tags_)
1859 if ([tag hasPrefix:@"purpose::"])
1860 return [tag substringFromIndex:9];
1864 - (NSArray *) purposes {
1865 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1866 for (NSString *tag in tags_)
1867 if ([tag hasPrefix:@"purpose::"])
1868 [purposes addObject:[tag substringFromIndex:9]];
1869 return [purposes count] == 0 ? nil : purposes;
1872 - (NSComparisonResult) compareByName:(Package *)package {
1873 NSString *lhs = [self name];
1874 NSString *rhs = [package name];
1876 if ([lhs length] != 0 && [rhs length] != 0) {
1877 unichar lhc = [lhs characterAtIndex:0];
1878 unichar rhc = [rhs characterAtIndex:0];
1880 if (isalpha(lhc) && !isalpha(rhc))
1881 return NSOrderedAscending;
1882 else if (!isalpha(lhc) && isalpha(rhc))
1883 return NSOrderedDescending;
1886 return [lhs compare:rhs options:LaxCompareOptions_];
1889 - (NSComparisonResult) compareBySection:(Package *)package {
1890 NSString *lhs = [self section];
1891 NSString *rhs = [package section];
1893 if (lhs == NULL && rhs != NULL)
1894 return NSOrderedAscending;
1895 else if (lhs != NULL && rhs == NULL)
1896 return NSOrderedDescending;
1897 else if (lhs != NULL && rhs != NULL) {
1898 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1899 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
1902 return NSOrderedSame;
1905 - (uint32_t) compareForChanges {
1910 uint32_t timestamp : 30;
1911 uint32_t ignored : 1;
1912 uint32_t upgradable : 1;
1916 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
1918 if ([self upgradableAndEssential:YES]) {
1919 value.bits.timestamp = 0;
1920 value.bits.ignored = [self ignored] ? 0 : 1;
1921 value.bits.upgradable = 1;
1923 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1924 value.bits.ignored = 0;
1925 value.bits.upgradable = 0;
1928 return _not(uint32_t) - value.key;
1932 pkgProblemResolver *resolver = [database_ resolver];
1933 resolver->Clear(iterator_);
1934 resolver->Protect(iterator_);
1935 pkgCacheFile &cache([database_ cache]);
1936 cache->MarkInstall(iterator_, false);
1937 pkgDepCache::StateCache &state((*cache)[iterator_]);
1938 if (!state.Install())
1939 cache->SetReInstall(iterator_, true);
1943 pkgProblemResolver *resolver = [database_ resolver];
1944 resolver->Clear(iterator_);
1945 resolver->Protect(iterator_);
1946 resolver->Remove(iterator_);
1947 [database_ cache]->MarkDelete(iterator_, true);
1950 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1951 return [NSNumber numberWithBool:(
1952 [self unfiltered] && [self matches:search]
1956 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1957 return [NSNumber numberWithBool:(
1958 (![number boolValue] || [self visible]) && [self installed] != nil
1962 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1963 NSString *section = [self section];
1965 return [NSNumber numberWithBool:(
1967 [self installed] == nil && (
1969 section == nil && [name length] == 0 ||
1970 [name isEqualToString:section]
1975 - (NSNumber *) isVisibleInSource:(Source *)source {
1976 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1981 /* Section Class {{{ */
1982 @interface Section : NSObject {
1988 - (NSComparisonResult) compareByName:(Section *)section;
1989 - (Section *) initWithName:(NSString *)name;
1990 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1991 - (NSString *) name;
1994 - (void) addToCount;
1998 @implementation Section
2005 - (NSComparisonResult) compareByName:(Section *)section {
2006 NSString *lhs = [self name];
2007 NSString *rhs = [section name];
2009 if ([lhs length] != 0 && [rhs length] != 0) {
2010 unichar lhc = [lhs characterAtIndex:0];
2011 unichar rhc = [rhs characterAtIndex:0];
2013 if (isalpha(lhc) && !isalpha(rhc))
2014 return NSOrderedAscending;
2015 else if (!isalpha(lhc) && isalpha(rhc))
2016 return NSOrderedDescending;
2019 return [lhs compare:rhs options:LaxCompareOptions_];
2022 - (Section *) initWithName:(NSString *)name {
2023 return [self initWithName:name row:0];
2026 - (Section *) initWithName:(NSString *)name row:(size_t)row {
2027 if ((self = [super init]) != nil) {
2028 name_ = [name retain];
2033 - (NSString *) name {
2045 - (void) addToCount {
2053 static NSArray *Finishes_;
2055 /* Database Implementation {{{ */
2056 @implementation Database
2058 + (Database *) sharedInstance {
2059 static Database *instance;
2060 if (instance == nil)
2061 instance = [[Database alloc] init];
2070 - (void) _readCydia:(NSNumber *)fd { _pooled
2071 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2072 std::istream is(&ib);
2075 static Pcre finish_r("^finish:([^:]*)$");
2077 while (std::getline(is, line)) {
2078 const char *data(line.c_str());
2079 size_t size = line.size();
2080 lprintf("C:%s\n", data);
2082 if (finish_r(data, size)) {
2083 NSString *finish = finish_r[1];
2084 int index = [Finishes_ indexOfObject:finish];
2085 if (index != INT_MAX && index > Finish_)
2093 - (void) _readStatus:(NSNumber *)fd { _pooled
2094 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2095 std::istream is(&ib);
2098 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2099 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
2101 while (std::getline(is, line)) {
2102 const char *data(line.c_str());
2103 size_t size = line.size();
2104 lprintf("S:%s\n", data);
2106 if (conffile_r(data, size)) {
2107 [delegate_ setConfigurationData:conffile_r[1]];
2108 } else if (strncmp(data, "status: ", 8) == 0) {
2109 NSString *string = [NSString stringWithUTF8String:(data + 8)];
2110 [delegate_ setProgressTitle:string];
2111 } else if (pmstatus_r(data, size)) {
2112 std::string type([pmstatus_r[1] UTF8String]);
2113 NSString *id = pmstatus_r[2];
2115 float percent([pmstatus_r[3] floatValue]);
2116 [delegate_ setProgressPercent:(percent / 100)];
2118 NSString *string = pmstatus_r[4];
2120 if (type == "pmerror")
2121 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2122 withObject:[NSArray arrayWithObjects:string, id, nil]
2125 else if (type == "pmstatus") {
2126 [delegate_ setProgressTitle:string];
2127 } else if (type == "pmconffile")
2128 [delegate_ setConfigurationData:string];
2129 else _assert(false);
2130 } else _assert(false);
2136 - (void) _readOutput:(NSNumber *)fd { _pooled
2137 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2138 std::istream is(&ib);
2141 while (std::getline(is, line)) {
2142 lprintf("O:%s\n", line.c_str());
2143 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
2153 - (Package *) packageWithName:(NSString *)name {
2154 if (static_cast<pkgDepCache *>(cache_) == NULL)
2156 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2157 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2160 - (Database *) init {
2161 if ((self = [super init]) != nil) {
2168 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2169 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2173 _assert(pipe(fds) != -1);
2176 _config->Set("APT::Keep-Fds::", cydiafd_);
2177 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
2180 detachNewThreadSelector:@selector(_readCydia:)
2182 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2185 _assert(pipe(fds) != -1);
2189 detachNewThreadSelector:@selector(_readStatus:)
2191 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2194 _assert(pipe(fds) != -1);
2195 _assert(dup2(fds[0], 0) != -1);
2196 _assert(close(fds[0]) != -1);
2198 input_ = fdopen(fds[1], "a");
2200 _assert(pipe(fds) != -1);
2201 _assert(dup2(fds[1], 1) != -1);
2202 _assert(close(fds[1]) != -1);
2205 detachNewThreadSelector:@selector(_readOutput:)
2207 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2212 - (pkgCacheFile &) cache {
2216 - (pkgDepCache::Policy *) policy {
2220 - (pkgRecords *) records {
2224 - (pkgProblemResolver *) resolver {
2228 - (pkgAcquire &) fetcher {
2232 - (NSArray *) packages {
2236 - (NSArray *) sources {
2237 return [sources_ allValues];
2240 - (NSArray *) issues {
2241 if (cache_->BrokenCount() == 0)
2244 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2246 for (Package *package in packages_) {
2247 if (![package broken])
2249 pkgCache::PkgIterator pkg([package iterator]);
2251 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2252 [entry addObject:[package name]];
2253 [issues addObject:entry];
2255 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2259 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2260 pkgCache::DepIterator start;
2261 pkgCache::DepIterator end;
2262 dep.GlobOr(start, end); // ++dep
2264 if (!cache_->IsImportantDep(end))
2266 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2269 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2270 [entry addObject:failure];
2271 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2273 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2274 [failure addObject:[package name]];
2276 pkgCache::PkgIterator target(start.TargetPkg());
2277 if (target->ProvidesList != 0)
2278 [failure addObject:@"?"];
2280 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2282 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2283 else if (!cache_[target].CandidateVerIter(cache_).end())
2284 [failure addObject:@"-"];
2285 else if (target->ProvidesList == 0)
2286 [failure addObject:@"!"];
2288 [failure addObject:@"%"];
2292 if (start.TargetVer() != 0)
2293 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2304 - (void) reloadData {
2324 if (!cache_.Open(progress_, true)) {
2326 if (!_error->PopMessage(error))
2329 lprintf("cache_.Open():[%s]\n", error.c_str());
2331 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2332 [delegate_ repairWithSelector:@selector(configure)];
2333 else if (error == "The package lists or status file could not be parsed or opened.")
2334 [delegate_ repairWithSelector:@selector(update)];
2335 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2336 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2337 // else if (error == "The list of sources could not be read.")
2338 else _assert(false);
2344 now_ = [[NSDate date] retain];
2346 policy_ = new pkgDepCache::Policy();
2347 records_ = new pkgRecords(cache_);
2348 resolver_ = new pkgProblemResolver(cache_);
2349 fetcher_ = new pkgAcquire(&status_);
2352 list_ = new pkgSourceList();
2353 _assert(list_->ReadMainList());
2355 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2356 _assert(pkgApplyStatus(cache_));
2358 if (cache_->BrokenCount() != 0) {
2359 _assert(pkgFixBroken(cache_));
2360 _assert(cache_->BrokenCount() == 0);
2361 _assert(pkgMinimizeUpgrade(cache_));
2364 [sources_ removeAllObjects];
2365 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2366 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2367 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2369 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2370 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2374 [packages_ removeAllObjects];
2377 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2378 if (Package *package = [Package packageWithIterator:iterator database:self])
2379 [packages_ addObject:package];
2381 [packages_ sortUsingSelector:@selector(compareByName:)];
2385 - (void) configure {
2386 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2387 system([dpkg UTF8String]);
2395 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2396 _assert(!_error->PendingError());
2399 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2402 public pkgArchiveCleaner
2405 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2410 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2412 while (_error->PopMessage(error))
2413 lprintf("ArchiveCleaner: %s\n", error.c_str());
2418 pkgRecords records(cache_);
2420 lock_ = new FileFd();
2421 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2422 _assert(!_error->PendingError());
2425 // XXX: explain this with an error message
2426 _assert(list.ReadMainList());
2428 manager_ = (_system->CreatePM(cache_));
2429 _assert(manager_->GetArchives(fetcher_, &list, &records));
2430 _assert(!_error->PendingError());
2434 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2436 _assert(list.ReadMainList());
2437 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2438 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2441 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2446 bool failed = false;
2447 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2448 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2451 std::string uri = (*item)->DescURI();
2452 std::string error = (*item)->ErrorText;
2454 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2457 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2458 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2469 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2471 if (_error->PendingError()) {
2476 if (result == pkgPackageManager::Failed) {
2481 if (result != pkgPackageManager::Completed) {
2486 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2488 _assert(list.ReadMainList());
2489 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2490 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2493 if (![before isEqualToArray:after])
2498 _assert(pkgDistUpgrade(cache_));
2502 [self updateWithStatus:status_];
2505 - (void) updateWithStatus:(Status &)status {
2507 _assert(list.ReadMainList());
2510 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2511 _assert(!_error->PendingError());
2513 pkgAcquire fetcher(&status);
2514 _assert(list.GetIndexes(&fetcher));
2516 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2517 bool failed = false;
2518 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2519 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2520 (*item)->Finished();
2524 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2525 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2526 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2529 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2534 - (void) setDelegate:(id)delegate {
2535 delegate_ = delegate;
2536 status_.setDelegate(delegate);
2537 progress_.setDelegate(delegate);
2540 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2541 pkgIndexFile *index(NULL);
2542 list_->FindIndex(file, index);
2543 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2549 /* PopUp Windows {{{ */
2550 @interface PopUpView : UIView {
2551 _transient id delegate_;
2552 UITransitionView *transition_;
2557 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2561 @implementation PopUpView
2564 [transition_ setDelegate:nil];
2565 [transition_ release];
2571 [transition_ transition:UITransitionPushFromTop toView:nil];
2574 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2575 if (from != nil && to == nil)
2576 [self removeFromSuperview];
2579 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2580 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2581 delegate_ = delegate;
2583 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2584 [self addSubview:transition_];
2586 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2588 [view addSubview:self];
2590 [transition_ setDelegate:self];
2592 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2593 [transition_ transition:UITransitionNone toView:blank];
2594 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2601 /* Mail Composition {{{ */
2602 @interface MailToView : PopUpView {
2603 MailComposeController *controller_;
2606 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2610 @implementation MailToView
2613 [controller_ release];
2617 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2621 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2622 NSLog(@"did:%@", delivery);
2623 // [UIApp setStatusBarShowsProgress:NO];
2624 if ([controller error]){
2625 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2626 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2627 [mailAlertSheet setBodyText:[controller error]];
2628 [mailAlertSheet popupAlertAnimated:YES];
2632 - (void) showError {
2633 NSLog(@"%@", [controller_ error]);
2634 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2635 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2636 [mailAlertSheet setBodyText:[controller_ error]];
2637 [mailAlertSheet popupAlertAnimated:YES];
2640 - (void) deliverMessage { _pooled
2644 if (![controller_ deliverMessage])
2645 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2648 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2649 if ([controller_ needsDelivery])
2650 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2655 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2656 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2657 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2658 [controller_ setDelegate:self];
2659 [controller_ initializeUI];
2660 [controller_ setupForURL:url];
2662 UIView *view([controller_ view]);
2663 [overlay_ addSubview:view];
2669 /* Confirmation View {{{ */
2670 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2671 if (!iterator.end())
2672 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2673 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2675 pkgCache::PkgIterator package(dep.TargetPkg());
2678 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2685 @protocol ConfirmationViewDelegate
2690 @interface ConfirmationView : BrowserView {
2691 _transient Database *database_;
2692 UIActionSheet *essential_;
2699 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2703 @implementation ConfirmationView
2710 if (essential_ != nil)
2711 [essential_ release];
2717 [book_ popFromSuperviewAnimated:YES];
2720 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2721 NSString *context([sheet context]);
2723 if ([context isEqualToString:@"remove"]) {
2731 [delegate_ confirm];
2738 } else if ([context isEqualToString:@"unable"]) {
2742 [super alertSheet:sheet buttonClicked:button];
2745 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2746 [window setValue:changes_ forKey:@"changes"];
2747 [window setValue:issues_ forKey:@"issues"];
2748 [window setValue:sizes_ forKey:@"sizes"];
2749 [super webView:sender didClearWindowObject:window forFrame:frame];
2752 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2753 if ((self = [super initWithBook:book]) != nil) {
2754 database_ = database;
2756 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2757 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2758 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2759 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2760 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2764 pkgDepCache::Policy *policy([database_ policy]);
2766 pkgCacheFile &cache([database_ cache]);
2767 NSArray *packages = [database_ packages];
2768 for (size_t i(0), e = [packages count]; i != e; ++i) {
2769 Package *package = [packages objectAtIndex:i];
2770 pkgCache::PkgIterator iterator = [package iterator];
2771 pkgDepCache::StateCache &state(cache[iterator]);
2773 NSString *name([package name]);
2775 if (state.NewInstall())
2776 [installing addObject:name];
2777 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2778 [reinstalling addObject:name];
2779 else if (state.Upgrade())
2780 [upgrading addObject:name];
2781 else if (state.Downgrade())
2782 [downgrading addObject:name];
2783 else if (state.Delete()) {
2784 if ([package essential])
2786 [removing addObject:name];
2789 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2790 substrate_ |= DepSubstrate(iterator.CurrentVer());
2795 else if (Advanced_ || true) {
2796 essential_ = [[UIActionSheet alloc]
2797 initWithTitle:@"Removing Essentials"
2798 buttons:[NSArray arrayWithObjects:
2799 @"Cancel Operation (Safe)",
2800 @"Force Removal (Unsafe)",
2802 defaultButtonIndex:0
2808 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2810 [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."];
2812 essential_ = [[UIActionSheet alloc]
2813 initWithTitle:@"Unable to Comply"
2814 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2815 defaultButtonIndex:0
2820 [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."];
2823 changes_ = [[NSArray alloc] initWithObjects:
2831 issues_ = [database_ issues];
2833 issues_ = [issues_ retain];
2835 sizes_ = [[NSArray alloc] initWithObjects:
2836 SizeString([database_ fetcher].FetchNeeded()),
2837 SizeString([database_ fetcher].PartialPresent()),
2838 SizeString([database_ cache]->UsrSize()),
2841 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2845 - (NSString *) backButtonTitle {
2849 - (NSString *) leftButtonTitle {
2853 - (id) _rightButtonTitle {
2854 return issues_ == nil ? @"Confirm" : nil;
2857 - (void) _leftButtonClicked {
2862 - (void) _rightButtonClicked {
2863 if (essential_ != nil)
2864 [essential_ popupAlertAnimated:YES];
2868 [delegate_ confirm];
2876 /* Progress Data {{{ */
2877 @interface ProgressData : NSObject {
2883 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2890 @implementation ProgressData
2892 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2893 if ((self = [super init]) != nil) {
2894 selector_ = selector;
2914 /* Progress View {{{ */
2915 @interface ProgressView : UIView <
2916 ConfigurationDelegate,
2919 _transient Database *database_;
2921 UIView *background_;
2922 UITransitionView *transition_;
2924 UINavigationBar *navbar_;
2925 UIProgressBar *progress_;
2926 UITextView *output_;
2927 UITextLabel *status_;
2928 UIPushButton *close_;
2931 SHA1SumValue springlist_;
2932 SHA1SumValue notifyconf_;
2933 SHA1SumValue sandplate_;
2935 NSTimeInterval last_;
2938 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2940 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2941 - (void) setContentView:(UIView *)view;
2944 - (void) _retachThread;
2945 - (void) _detachNewThreadData:(ProgressData *)data;
2946 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2952 @protocol ProgressViewDelegate
2953 - (void) progressViewIsComplete:(ProgressView *)sender;
2956 @implementation ProgressView
2959 [transition_ setDelegate:nil];
2960 [navbar_ setDelegate:nil];
2963 if (background_ != nil)
2964 [background_ release];
2965 [transition_ release];
2968 [progress_ release];
2975 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2976 if (bootstrap_ && from == overlay_ && to == view_)
2980 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2981 if ((self = [super initWithFrame:frame]) != nil) {
2982 database_ = database;
2983 delegate_ = delegate;
2985 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2986 [transition_ setDelegate:self];
2988 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2991 [overlay_ setBackgroundColor:[UIColor blackColor]];
2993 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2994 [background_ setBackgroundColor:[UIColor blackColor]];
2995 [self addSubview:background_];
2998 [self addSubview:transition_];
3000 CGSize navsize = [UINavigationBar defaultSize];
3001 CGRect navrect = {{0, 0}, navsize};
3003 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3004 [overlay_ addSubview:navbar_];
3006 [navbar_ setBarStyle:1];
3007 [navbar_ setDelegate:self];
3009 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3010 [navbar_ pushNavigationItem:navitem];
3012 CGRect bounds = [overlay_ bounds];
3013 CGSize prgsize = [UIProgressBar defaultSize];
3016 (bounds.size.width - prgsize.width) / 2,
3017 bounds.size.height - prgsize.height - 20
3020 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3021 [progress_ setStyle:0];
3023 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3025 bounds.size.height - prgsize.height - 50,
3026 bounds.size.width - 20,
3030 [status_ setColor:[UIColor whiteColor]];
3031 [status_ setBackgroundColor:[UIColor clearColor]];
3033 [status_ setCentersHorizontally:YES];
3034 //[status_ setFont:font];
3036 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3038 navrect.size.height + 20,
3039 bounds.size.width - 20,
3040 bounds.size.height - navsize.height - 62 - navrect.size.height
3043 //[output_ setTextFont:@"Courier New"];
3044 [output_ setTextSize:12];
3046 [output_ setTextColor:[UIColor whiteColor]];
3047 [output_ setBackgroundColor:[UIColor clearColor]];
3049 [output_ setMarginTop:0];
3050 [output_ setAllowsRubberBanding:YES];
3051 [output_ setEditable:NO];
3053 [overlay_ addSubview:output_];
3055 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3057 bounds.size.height - prgsize.height - 50,
3058 bounds.size.width - 20,
3062 [close_ setAutosizesToFit:NO];
3063 [close_ setDrawsShadow:YES];
3064 [close_ setStretchBackground:YES];
3065 [close_ setEnabled:YES];
3067 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3068 [close_ setTitleFont:bold];
3070 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3071 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3072 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3076 - (void) setContentView:(UIView *)view {
3077 view_ = [view retain];
3080 - (void) resetView {
3081 [transition_ transition:6 toView:view_];
3084 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3085 NSString *context([sheet context]);
3087 if ([context isEqualToString:@"conffile"]) {
3088 FILE *input = [database_ input];
3092 fprintf(input, "N\n");
3096 fprintf(input, "Y\n");
3107 - (void) closeButtonPushed {
3116 [delegate_ suspendWithAnimation:YES];
3120 system("launchctl stop com.apple.SpringBoard");
3124 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3133 - (void) _retachThread {
3134 UINavigationItem *item = [navbar_ topItem];
3135 [item setTitle:@"Complete"];
3137 [overlay_ addSubview:close_];
3138 [progress_ removeFromSuperview];
3139 [status_ removeFromSuperview];
3141 [delegate_ progressViewIsComplete:self];
3144 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3145 MMap mmap(file, MMap::ReadOnly);
3147 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3148 if (!(sandplate_ == sha1.Result()))
3153 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3154 MMap mmap(file, MMap::ReadOnly);
3156 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3157 if (!(notifyconf_ == sha1.Result()))
3162 FileFd file(SpringBoard_, FileFd::ReadOnly);
3163 MMap mmap(file, MMap::ReadOnly);
3165 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3166 if (!(springlist_ == sha1.Result()))
3171 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3172 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3173 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3174 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3175 case 4: [close_ setTitle:@"Reboot Device"]; break;
3178 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3180 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3181 [cache autorelease];
3183 NSFileManager *manager = [NSFileManager defaultManager];
3184 NSError *error = nil;
3186 id system = [cache objectForKey:@"System"];
3191 if (stat(Cache_, &info) == -1)
3194 [system removeAllObjects];
3196 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3197 for (NSString *app in apps)
3198 if ([app hasSuffix:@".app"]) {
3199 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3200 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3201 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3203 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3204 [info setObject:path forKey:@"Path"];
3205 [info setObject:@"System" forKey:@"ApplicationType"];
3206 [system addInfoDictionary:info];
3212 [cache writeToFile:@Cache_ atomically:YES];
3214 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3216 if (chmod(Cache_, info.st_mode) == -1)
3220 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3223 notify_post("com.apple.mobile.application_installed");
3225 [delegate_ setStatusBarShowsProgress:NO];
3228 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3229 [[data target] performSelector:[data selector] withObject:[data object]];
3232 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3235 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3236 UINavigationItem *item = [navbar_ topItem];
3237 [item setTitle:title];
3239 [status_ setText:nil];
3240 [output_ setText:@""];
3241 [progress_ setProgress:0];
3244 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3246 [close_ removeFromSuperview];
3247 [overlay_ addSubview:progress_];
3248 [overlay_ addSubview:status_];
3250 [delegate_ setStatusBarShowsProgress:YES];
3254 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3255 MMap mmap(file, MMap::ReadOnly);
3257 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3258 sandplate_ = sha1.Result();
3262 FileFd file(NotifyConfig_, FileFd::ReadOnly);
3263 MMap mmap(file, MMap::ReadOnly);
3265 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3266 notifyconf_ = sha1.Result();
3270 FileFd file(SpringBoard_, FileFd::ReadOnly);
3271 MMap mmap(file, MMap::ReadOnly);
3273 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3274 springlist_ = sha1.Result();
3277 [transition_ transition:6 toView:overlay_];
3280 detachNewThreadSelector:@selector(_detachNewThreadData:)
3282 withObject:[[ProgressData alloc]
3283 initWithSelector:selector
3290 - (void) repairWithSelector:(SEL)selector {
3292 detachNewThreadSelector:selector
3299 - (void) setConfigurationData:(NSString *)data {
3301 performSelectorOnMainThread:@selector(_setConfigurationData:)
3307 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3308 Package *package = id == nil ? nil : [database_ packageWithName:id];
3310 UIActionSheet *sheet = [[[UIActionSheet alloc]
3311 initWithTitle:(package == nil ? id : [package name])
3312 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3313 defaultButtonIndex:0
3318 [sheet setBodyText:error];
3319 [sheet popupAlertAnimated:YES];
3322 - (void) setProgressTitle:(NSString *)title {
3324 performSelectorOnMainThread:@selector(_setProgressTitle:)
3330 - (void) setProgressPercent:(float)percent {
3332 performSelectorOnMainThread:@selector(_setProgressPercent:)
3333 withObject:[NSNumber numberWithFloat:percent]
3338 - (void) startProgress {
3339 last_ = [NSDate timeIntervalSinceReferenceDate];
3342 - (void) addProgressOutput:(NSString *)output {
3344 performSelectorOnMainThread:@selector(_addProgressOutput:)
3350 - (bool) isCancelling:(size_t)received {
3352 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3353 if (received_ != received) {
3354 received_ = received;
3356 } else if (now - last_ > 30)
3363 - (void) _setConfigurationData:(NSString *)data {
3364 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3366 _assert(conffile_r(data));
3368 NSString *ofile = conffile_r[1];
3369 //NSString *nfile = conffile_r[2];
3371 UIActionSheet *sheet = [[[UIActionSheet alloc]
3372 initWithTitle:@"Configuration Upgrade"
3373 buttons:[NSArray arrayWithObjects:
3374 @"Keep My Old Copy",
3375 @"Accept The New Copy",
3376 // XXX: @"See What Changed",
3378 defaultButtonIndex:0
3383 [sheet setBodyText:[NSString stringWithFormat:
3384 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3387 [sheet popupAlertAnimated:YES];
3390 - (void) _setProgressTitle:(NSString *)title {
3391 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3392 for (size_t i(0), e([words count]); i != e; ++i) {
3393 NSString *word([words objectAtIndex:i]);
3394 if (Package *package = [database_ packageWithName:word])
3395 [words replaceObjectAtIndex:i withObject:[package name]];
3398 [status_ setText:[words componentsJoinedByString:@" "]];
3401 - (void) _setProgressPercent:(NSNumber *)percent {
3402 [progress_ setProgress:[percent floatValue]];
3405 - (void) _addProgressOutput:(NSString *)output {
3406 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3407 CGSize size = [output_ contentSize];
3408 CGRect rect = {{0, size.height}, {size.width, 0}};
3409 [output_ scrollRectToVisible:rect animated:YES];
3412 - (BOOL) isRunning {
3419 /* Package Cell {{{ */
3420 @interface PackageCell : UISimpleTableCell {
3423 NSString *description_;
3427 UITextLabel *status_;
3431 - (PackageCell *) init;
3432 - (void) setPackage:(Package *)package;
3434 + (int) heightForPackage:(Package *)package;
3438 @implementation PackageCell
3440 - (void) clearPackage {
3451 if (description_ != nil) {
3452 [description_ release];
3456 if (source_ != nil) {
3461 if (badge_ != nil) {
3468 [self clearPackage];
3475 - (PackageCell *) init {
3476 if ((self = [super init]) != nil) {
3478 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3479 [status_ setBackgroundColor:[UIColor clearColor]];
3480 [status_ setFont:small];
3485 - (void) setPackage:(Package *)package {
3486 [self clearPackage];
3488 Source *source = [package source];
3489 NSString *section = [package simpleSection];
3491 icon_ = [[package icon] retain];
3493 name_ = [[package name] retain];
3494 description_ = [[package tagline] retain];
3496 NSString *label = nil;
3497 bool trusted = false;
3499 if (source != nil) {
3500 label = [source label];
3501 trusted = [source trusted];
3502 } else if ([[package id] isEqualToString:@"firmware"])
3505 label = @"Unknown/Local";
3507 NSString *from = [NSString stringWithFormat:@"from %@", label];
3509 if (section != nil && ![section isEqualToString:label])
3510 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3512 source_ = [from retain];
3514 if (NSString *purpose = [package primaryPurpose])
3515 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3516 badge_ = [badge_ retain];
3519 if (NSString *mode = [package mode]) {
3520 [badge_ setImage:[UIImage applicationImageNamed:
3521 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3524 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3525 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3526 } else if ([package half]) {
3527 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3528 [status_ setText:@"Package Damaged"];
3529 [status_ setColor:[UIColor redColor]];
3531 [badge_ setImage:nil];
3532 [status_ setText:nil];
3537 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3540 rect.size = [icon_ size];
3542 rect.size.width /= 2;
3543 rect.size.height /= 2;
3545 rect.origin.x = 25 - rect.size.width / 2;
3546 rect.origin.y = 25 - rect.size.height / 2;
3548 [icon_ drawInRect:rect];
3551 if (badge_ != nil) {
3552 CGSize size = [badge_ size];
3554 [badge_ drawAtPoint:CGPointMake(
3555 36 - size.width / 2,
3556 36 - size.height / 2
3565 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3566 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3570 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3572 [super drawContentInRect:rect selected:selected];
3575 + (int) heightForPackage:(Package *)package {
3576 NSString *tagline([package tagline]);
3577 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3579 if ([package hasMode] || [package half])
3588 /* Section Cell {{{ */
3589 @interface SectionCell : UISimpleTableCell {
3594 _UISwitchSlider *switch_;
3599 - (void) setSection:(Section *)section editing:(BOOL)editing;
3603 @implementation SectionCell
3605 - (void) clearSection {
3606 if (section_ != nil) {
3616 if (count_ != nil) {
3623 [self clearSection];
3630 if ((self = [super init]) != nil) {
3631 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3633 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3634 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3638 - (void) onSwitch:(id)sender {
3639 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3640 if (metadata == nil) {
3641 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3642 [Sections_ setObject:metadata forKey:section_];
3646 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3649 - (void) setSection:(Section *)section editing:(BOOL)editing {
3650 if (editing != editing_) {
3652 [switch_ removeFromSuperview];
3654 [self addSubview:switch_];
3658 [self clearSection];
3660 if (section == nil) {
3661 name_ = [@"All Packages" retain];
3664 section_ = [section name];
3665 if (section_ != nil)
3666 section_ = [section_ retain];
3667 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3668 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3671 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3675 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3676 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3683 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3685 CGSize size = [count_ sizeWithFont:Font14_];
3689 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3691 [super drawContentInRect:rect selected:selected];
3697 /* File Table {{{ */
3698 @interface FileTable : RVPage {
3699 _transient Database *database_;
3702 NSMutableArray *files_;
3706 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3707 - (void) setPackage:(Package *)package;
3711 @implementation FileTable
3714 if (package_ != nil)
3723 - (int) numberOfRowsInTable:(UITable *)table {
3724 return files_ == nil ? 0 : [files_ count];
3727 - (float) table:(UITable *)table heightForRow:(int)row {
3731 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3732 if (reusing == nil) {
3733 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3734 UIFont *font = [UIFont systemFontOfSize:16];
3735 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3737 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3741 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3745 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3746 if ((self = [super initWithBook:book]) != nil) {
3747 database_ = database;
3749 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3751 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3752 [self addSubview:list_];
3754 UITableColumn *column = [[[UITableColumn alloc]
3755 initWithTitle:@"Name"
3757 width:[self frame].size.width
3760 [list_ setDataSource:self];
3761 [list_ setSeparatorStyle:1];
3762 [list_ addTableColumn:column];
3763 [list_ setDelegate:self];
3764 [list_ setReusesTableCells:YES];
3768 - (void) setPackage:(Package *)package {
3769 if (package_ != nil) {
3770 [package_ autorelease];
3779 [files_ removeAllObjects];
3781 if (package != nil) {
3782 package_ = [package retain];
3783 name_ = [[package id] retain];
3785 if (NSArray *files = [package files])
3786 [files_ addObjectsFromArray:files];
3788 if ([files_ count] != 0) {
3789 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3790 [files_ removeObjectAtIndex:0];
3791 [files_ sortUsingSelector:@selector(compareByPath:)];
3793 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3794 [stack addObject:@"/"];
3796 for (int i(0), e([files_ count]); i != e; ++i) {
3797 NSString *file = [files_ objectAtIndex:i];
3798 while (![file hasPrefix:[stack lastObject]])
3799 [stack removeLastObject];
3800 NSString *directory = [stack lastObject];
3801 [stack addObject:[file stringByAppendingString:@"/"]];
3802 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3803 ([stack count] - 2) * 3, "",
3804 [file substringFromIndex:[directory length]]
3813 - (void) resetViewAnimated:(BOOL)animated {
3814 [list_ resetViewAnimated:animated];
3817 - (void) reloadData {
3818 [self setPackage:[database_ packageWithName:name_]];
3819 [self reloadButtons];
3822 - (NSString *) title {
3823 return @"Installed Files";
3826 - (NSString *) backButtonTitle {
3832 /* Package View {{{ */
3833 @interface PackageView : BrowserView {
3834 _transient Database *database_;
3837 NSMutableArray *buttons_;
3840 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3841 - (void) setPackage:(Package *)package;
3845 @implementation PackageView
3848 if (package_ != nil)
3856 - (void) _clickButtonWithName:(NSString *)name {
3857 if ([name isEqualToString:@"Install"])
3858 [delegate_ installPackage:package_];
3859 else if ([name isEqualToString:@"Reinstall"])
3860 [delegate_ installPackage:package_];
3861 else if ([name isEqualToString:@"Remove"])
3862 [delegate_ removePackage:package_];
3863 else if ([name isEqualToString:@"Upgrade"])
3864 [delegate_ installPackage:package_];
3865 else _assert(false);
3868 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3869 NSString *context([sheet context]);
3871 if ([context isEqualToString:@"modify"]) {
3872 int count = [buttons_ count];
3873 _assert(count != 0);
3874 _assert(button <= count + 1);
3876 if (count != button - 1)
3877 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3881 [super alertSheet:sheet buttonClicked:button];
3884 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3885 return [super webView:sender didFinishLoadForFrame:frame];
3888 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3889 [window setValue:package_ forKey:@"package"];
3890 [super webView:sender didClearWindowObject:window forFrame:frame];
3894 - (void) _rightButtonClicked {
3895 /*[super _rightButtonClicked];
3898 int count = [buttons_ count];
3899 _assert(count != 0);
3902 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3904 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3905 [buttons addObjectsFromArray:buttons_];
3906 [buttons addObject:@"Cancel"];
3908 [delegate_ slideUp:[[[UIActionSheet alloc]
3911 defaultButtonIndex:2
3919 - (id) _rightButtonTitle {
3920 int count = [buttons_ count];
3921 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3924 - (NSString *) backButtonTitle {
3928 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3929 if ((self = [super initWithBook:book]) != nil) {
3930 database_ = database;
3931 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3935 - (void) setPackage:(Package *)package {
3936 if (package_ != nil) {
3937 [package_ autorelease];
3946 [buttons_ removeAllObjects];
3948 if (package != nil) {
3949 package_ = [package retain];
3950 name_ = [[package id] retain];
3952 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3954 if ([package_ source] == nil);
3955 else if ([package_ upgradableAndEssential:NO])
3956 [buttons_ addObject:@"Upgrade"];
3957 else if ([package_ installed] == nil)
3958 [buttons_ addObject:@"Install"];
3960 [buttons_ addObject:@"Reinstall"];
3961 if ([package_ installed] != nil)
3962 [buttons_ addObject:@"Remove"];
3970 - (void) reloadData {
3971 [self setPackage:[database_ packageWithName:name_]];
3972 [self reloadButtons];
3977 /* Package Table {{{ */
3978 @interface PackageTable : RVPage {
3979 _transient Database *database_;
3983 NSMutableArray *packages_;
3984 NSMutableArray *sections_;
3985 UISectionList *list_;
3988 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3990 - (void) setDelegate:(id)delegate;
3991 - (void) setObject:(id)object;
3993 - (void) reloadData;
3994 - (void) resetCursor;
3996 - (UISectionList *) list;
3998 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4002 @implementation PackageTable
4005 [list_ setDataSource:nil];
4010 [packages_ release];
4011 [sections_ release];
4016 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4017 return [sections_ count];
4020 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4021 return [[sections_ objectAtIndex:section] name];
4024 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4025 return [[sections_ objectAtIndex:section] row];
4028 - (int) numberOfRowsInTable:(UITable *)table {
4029 return [packages_ count];
4032 - (float) table:(UITable *)table heightForRow:(int)row {
4033 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4036 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4038 reusing = [[[PackageCell alloc] init] autorelease];
4039 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4043 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4047 - (void) tableRowSelected:(NSNotification *)notification {
4048 int row = [[notification object] selectedRow];
4052 Package *package = [packages_ objectAtIndex:row];
4053 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4054 [view setDelegate:delegate_];
4055 [view setPackage:package];
4056 [book_ pushPage:view];
4059 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4060 if ((self = [super initWithBook:book]) != nil) {
4061 database_ = database;
4062 title_ = [title retain];
4064 object_ = object == nil ? nil : [object retain];
4066 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4067 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4069 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4070 [list_ setDataSource:self];
4072 UITableColumn *column = [[[UITableColumn alloc]
4073 initWithTitle:@"Name"
4075 width:[self frame].size.width
4078 UITable *table = [list_ table];
4079 [table setSeparatorStyle:1];
4080 [table addTableColumn:column];
4081 [table setDelegate:self];
4082 [table setReusesTableCells:YES];
4084 [self addSubview:list_];
4087 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4088 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4092 - (void) setDelegate:(id)delegate {
4093 delegate_ = delegate;
4096 - (void) setObject:(id)object {
4102 object_ = [object retain];
4105 - (void) reloadData {
4106 NSArray *packages = [database_ packages];
4108 [packages_ removeAllObjects];
4109 [sections_ removeAllObjects];
4111 for (size_t i(0); i != [packages count]; ++i) {
4112 Package *package([packages objectAtIndex:i]);
4113 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4114 [packages_ addObject:package];
4117 Section *section = nil;
4119 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4120 Package *package = [packages_ objectAtIndex:offset];
4121 NSString *name = [package index];
4123 if (section == nil || ![[section name] isEqualToString:name]) {
4124 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4125 [sections_ addObject:section];
4128 [section addToCount];
4134 - (NSString *) title {
4138 - (void) resetViewAnimated:(BOOL)animated {
4139 [list_ resetViewAnimated:animated];
4142 - (void) resetCursor {
4143 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4146 - (UISectionList *) list {
4150 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4151 [list_ setShouldHideHeaderInShortLists:hide];
4157 /* Add Source View {{{ */
4158 @interface AddSourceView : RVPage {
4159 _transient Database *database_;
4162 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4166 @implementation AddSourceView
4168 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4169 if ((self = [super initWithBook:book]) != nil) {
4170 database_ = database;
4176 /* Source Cell {{{ */
4177 @interface SourceCell : UITableCell {
4180 NSString *description_;
4186 - (SourceCell *) initWithSource:(Source *)source;
4190 @implementation SourceCell
4195 [description_ release];
4200 - (SourceCell *) initWithSource:(Source *)source {
4201 if ((self = [super init]) != nil) {
4203 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4205 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4206 icon_ = [icon_ retain];
4208 origin_ = [[source name] retain];
4209 label_ = [[source uri] retain];
4210 description_ = [[source description] retain];
4214 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4216 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4223 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4227 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4231 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4233 [super drawContentInRect:rect selected:selected];
4238 /* Source Table {{{ */
4239 @interface SourceTable : RVPage {
4240 _transient Database *database_;
4241 UISectionList *list_;
4242 NSMutableArray *sources_;
4243 UIActionSheet *alert_;
4247 UIProgressHUD *hud_;
4250 //NSURLConnection *installer_;
4251 NSURLConnection *trivial_bz2_;
4252 NSURLConnection *trivial_gz_;
4253 //NSURLConnection *automatic_;
4258 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4262 @implementation SourceTable
4264 - (void) _deallocConnection:(NSURLConnection *)connection {
4265 if (connection != nil) {
4266 [connection cancel];
4267 //[connection setDelegate:nil];
4268 [connection release];
4273 [[list_ table] setDelegate:nil];
4274 [list_ setDataSource:nil];
4283 //[self _deallocConnection:installer_];
4284 [self _deallocConnection:trivial_gz_];
4285 [self _deallocConnection:trivial_bz2_];
4286 //[self _deallocConnection:automatic_];
4293 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4294 return offset_ == 0 ? 1 : 2;
4297 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4298 switch (section + (offset_ == 0 ? 1 : 0)) {
4299 case 0: return @"Entered by User";
4300 case 1: return @"Installed by Packages";
4308 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4309 switch (section + (offset_ == 0 ? 1 : 0)) {
4311 case 1: return offset_;
4319 - (int) numberOfRowsInTable:(UITable *)table {
4320 return [sources_ count];
4323 - (float) table:(UITable *)table heightForRow:(int)row {
4324 Source *source = [sources_ objectAtIndex:row];
4325 return [source description] == nil ? 56 : 73;
4328 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4329 Source *source = [sources_ objectAtIndex:row];
4330 // XXX: weird warning, stupid selectors ;P
4331 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4334 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4338 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4342 - (void) tableRowSelected:(NSNotification*)notification {
4343 UITable *table([list_ table]);
4344 int row([table selectedRow]);
4348 Source *source = [sources_ objectAtIndex:row];
4350 PackageTable *packages = [[[PackageTable alloc]
4353 title:[source label]
4354 filter:@selector(isVisibleInSource:)
4358 [packages setDelegate:delegate_];
4360 [book_ pushPage:packages];
4363 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4364 Source *source = [sources_ objectAtIndex:row];
4365 return [source record] != nil;
4368 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4369 [[list_ table] setDeleteConfirmationRow:row];
4372 - (void) table:(UITable *)table deleteRow:(int)row {
4373 Source *source = [sources_ objectAtIndex:row];
4374 [Sources_ removeObjectForKey:[source key]];
4375 [delegate_ syncData];
4378 - (void) _endConnection:(NSURLConnection *)connection {
4379 NSURLConnection **field = NULL;
4380 if (connection == trivial_bz2_)
4381 field = &trivial_bz2_;
4382 else if (connection == trivial_gz_)
4383 field = &trivial_gz_;
4384 _assert(field != NULL);
4385 [connection release];
4389 trivial_bz2_ == nil &&
4392 [delegate_ setStatusBarShowsProgress:NO];
4395 [hud_ removeFromSuperview];
4400 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4403 @"./", @"Distribution",
4404 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4406 [delegate_ syncData];
4407 } else if (error_ != nil) {
4408 UIActionSheet *sheet = [[[UIActionSheet alloc]
4409 initWithTitle:@"Verification Error"
4410 buttons:[NSArray arrayWithObjects:@"OK", nil]
4411 defaultButtonIndex:0
4416 [sheet setBodyText:[error_ localizedDescription]];
4417 [sheet popupAlertAnimated:YES];
4419 UIActionSheet *sheet = [[[UIActionSheet alloc]
4420 initWithTitle:@"Did not Find Repository"
4421 buttons:[NSArray arrayWithObjects:@"OK", nil]
4422 defaultButtonIndex:0
4427 [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."];
4428 [sheet popupAlertAnimated:YES];
4434 if (error_ != nil) {
4441 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4442 switch ([response statusCode]) {
4448 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4449 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4451 error_ = [error retain];
4452 [self _endConnection:connection];
4455 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4456 [self _endConnection:connection];
4459 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4460 NSMutableURLRequest *request = [NSMutableURLRequest
4461 requestWithURL:[NSURL URLWithString:href]
4462 cachePolicy:NSURLRequestUseProtocolCachePolicy
4463 timeoutInterval:20.0
4466 [request setHTTPMethod:method];
4468 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4471 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4472 NSString *context([sheet context]);
4474 if ([context isEqualToString:@"source"]) {
4477 NSString *href = [[sheet textField] text];
4479 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4481 if (![href hasSuffix:@"/"])
4482 href_ = [href stringByAppendingString:@"/"];
4485 href_ = [href_ retain];
4487 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4488 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4489 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4493 hud_ = [delegate_ addProgressHUD];
4494 [hud_ setText:@"Verifying URL"];
4505 } else if ([context isEqualToString:@"trivial"])
4507 else if ([context isEqualToString:@"urlerror"])
4511 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4512 if ((self = [super initWithBook:book]) != nil) {
4513 database_ = database;
4514 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4516 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4517 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4518 [list_ setShouldHideHeaderInShortLists:NO];
4520 [self addSubview:list_];
4521 [list_ setDataSource:self];
4523 UITableColumn *column = [[UITableColumn alloc]
4524 initWithTitle:@"Name"
4526 width:[self frame].size.width
4529 UITable *table = [list_ table];
4530 [table setSeparatorStyle:1];
4531 [table addTableColumn:column];
4532 [table setDelegate:self];
4536 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4537 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4541 - (void) reloadData {
4543 _assert(list.ReadMainList());
4545 [sources_ removeAllObjects];
4546 [sources_ addObjectsFromArray:[database_ sources]];
4548 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4551 int count = [sources_ count];
4552 for (offset_ = 0; offset_ != count; ++offset_) {
4553 Source *source = [sources_ objectAtIndex:offset_];
4554 if ([source record] == nil)
4561 - (void) resetViewAnimated:(BOOL)animated {
4562 [list_ resetViewAnimated:animated];
4565 - (void) _leftButtonClicked {
4566 /*[book_ pushPage:[[[AddSourceView alloc]
4571 UIActionSheet *sheet = [[[UIActionSheet alloc]
4572 initWithTitle:@"Enter Cydia/APT URL"
4573 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4574 defaultButtonIndex:0
4579 [sheet setNumberOfRows:1];
4581 [sheet addTextFieldWithValue:@"http://" label:@""];
4583 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4584 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4585 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
4586 [traits setKeyboardType:UIKeyboardTypeURL];
4587 // XXX: UIReturnKeyDone
4588 [traits setReturnKeyType:UIReturnKeyNext];
4590 [sheet popupAlertAnimated:YES];
4593 - (void) _rightButtonClicked {
4594 UITable *table = [list_ table];
4595 BOOL editing = [table isRowDeletionEnabled];
4596 [table enableRowDeletion:!editing animated:YES];
4597 [book_ reloadButtonsForPage:self];
4600 - (NSString *) title {
4604 - (NSString *) leftButtonTitle {
4605 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4608 - (id) rightButtonTitle {
4609 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4612 - (UINavigationButtonStyle) rightButtonStyle {
4613 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4619 /* Installed View {{{ */
4620 @interface InstalledView : RVPage {
4621 _transient Database *database_;
4622 PackageTable *packages_;
4626 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4630 @implementation InstalledView
4633 [packages_ release];
4637 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4638 if ((self = [super initWithBook:book]) != nil) {
4639 database_ = database;
4641 packages_ = [[PackageTable alloc]
4645 filter:@selector(isInstalledAndVisible:)
4646 with:[NSNumber numberWithBool:YES]
4649 [self addSubview:packages_];
4651 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4652 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4656 - (void) resetViewAnimated:(BOOL)animated {
4657 [packages_ resetViewAnimated:animated];
4660 - (void) reloadData {
4661 [packages_ reloadData];
4664 - (void) _rightButtonClicked {
4665 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4666 [packages_ reloadData];
4668 [book_ reloadButtonsForPage:self];
4671 - (NSString *) title {
4672 return @"Installed";
4675 - (NSString *) backButtonTitle {
4679 - (id) rightButtonTitle {
4680 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4683 - (UINavigationButtonStyle) rightButtonStyle {
4684 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4687 - (void) setDelegate:(id)delegate {
4688 [super setDelegate:delegate];
4689 [packages_ setDelegate:delegate];
4696 @interface HomeView : BrowserView {
4701 @implementation HomeView
4703 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4704 NSString *context([sheet context]);
4706 if ([context isEqualToString:@"about"])
4709 [super alertSheet:sheet buttonClicked:button];
4712 - (void) _leftButtonClicked {
4713 UIActionSheet *sheet = [[[UIActionSheet alloc]
4714 initWithTitle:@"About Cydia Installer"
4715 buttons:[NSArray arrayWithObjects:@"Close", nil]
4716 defaultButtonIndex:0
4722 @"Copyright (C) 2008\n"
4723 "Jay Freeman (saurik)\n"
4724 "saurik@saurik.com\n"
4725 "http://www.saurik.com/\n"
4728 "http://www.theokorigroup.com/\n"
4730 "College of Creative Studies,\n"
4731 "University of California,\n"
4733 "http://www.ccs.ucsb.edu/"
4736 [sheet popupAlertAnimated:YES];
4739 - (NSString *) leftButtonTitle {
4745 /* Manage View {{{ */
4746 @interface ManageView : BrowserView {
4751 @implementation ManageView
4753 - (NSString *) title {
4757 - (void) _leftButtonClicked {
4758 [delegate_ askForSettings];
4761 - (NSString *) leftButtonTitle {
4766 - (id) _rightButtonTitle {
4778 /* Indirect Delegate {{{ */
4779 @interface IndirectDelegate : NSProxy {
4780 _transient volatile id delegate_;
4783 - (void) setDelegate:(id)delegate;
4784 - (id) initWithDelegate:(id)delegate;
4787 @implementation IndirectDelegate
4789 - (void) setDelegate:(id)delegate {
4790 delegate_ = delegate;
4793 - (id) initWithDelegate:(id)delegate {
4794 delegate_ = delegate;
4798 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4799 if (delegate_ != nil)
4800 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4802 // XXX: I fucking hate Apple so very very bad
4803 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4806 - (void) forwardInvocation:(NSInvocation *)inv {
4807 SEL sel = [inv selector];
4808 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4809 [inv invokeWithTarget:delegate_];
4815 #include <BrowserView.m>
4817 /* Cydia Book {{{ */
4818 @interface CYBook : RVBook <
4821 _transient Database *database_;
4822 UINavigationBar *overlay_;
4823 UINavigationBar *underlay_;
4824 UIProgressIndicator *indicator_;
4825 UITextLabel *prompt_;
4826 UIProgressBar *progress_;
4827 UINavigationButton *cancel_;
4830 NSTimeInterval last_;
4833 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4839 @implementation CYBook
4843 [indicator_ release];
4845 [progress_ release];
4850 - (NSString *) getTitleForPage:(RVPage *)page {
4851 return Simplify([super getTitleForPage:page]);
4859 [UIView beginAnimations:nil context:NULL];
4861 CGRect ovrframe = [overlay_ frame];
4862 ovrframe.origin.y = 0;
4863 [overlay_ setFrame:ovrframe];
4865 CGRect barframe = [navbar_ frame];
4866 barframe.origin.y += ovrframe.size.height;
4867 [navbar_ setFrame:barframe];
4869 CGRect trnframe = [transition_ frame];
4870 trnframe.origin.y += ovrframe.size.height;
4871 trnframe.size.height -= ovrframe.size.height;
4872 [transition_ setFrame:trnframe];
4874 [UIView endAnimations];
4876 [indicator_ startAnimation];
4877 [prompt_ setText:@"Updating Database"];
4878 [progress_ setProgress:0];
4881 last_ = [NSDate timeIntervalSinceReferenceDate];
4883 [overlay_ addSubview:cancel_];
4886 detachNewThreadSelector:@selector(_update)
4895 [indicator_ stopAnimation];
4897 [UIView beginAnimations:nil context:NULL];
4899 CGRect ovrframe = [overlay_ frame];
4900 ovrframe.origin.y = -ovrframe.size.height;
4901 [overlay_ setFrame:ovrframe];
4903 CGRect barframe = [navbar_ frame];
4904 barframe.origin.y -= ovrframe.size.height;
4905 [navbar_ setFrame:barframe];
4907 CGRect trnframe = [transition_ frame];
4908 trnframe.origin.y -= ovrframe.size.height;
4909 trnframe.size.height += ovrframe.size.height;
4910 [transition_ setFrame:trnframe];
4912 [UIView commitAnimations];
4914 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4917 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4918 if ((self = [super initWithFrame:frame]) != nil) {
4919 database_ = database;
4921 CGRect ovrrect = [navbar_ bounds];
4922 ovrrect.size.height = [UINavigationBar defaultSize].height;
4923 ovrrect.origin.y = -ovrrect.size.height;
4925 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4926 [self addSubview:overlay_];
4928 ovrrect.origin.y = frame.size.height;
4929 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4930 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4931 [self addSubview:underlay_];
4933 [overlay_ setBarStyle:1];
4934 [underlay_ setBarStyle:1];
4936 int barstyle = [overlay_ _barStyle:NO];
4937 bool ugly = barstyle == 0;
4939 UIProgressIndicatorStyle style = ugly ?
4940 UIProgressIndicatorStyleMediumBrown :
4941 UIProgressIndicatorStyleMediumWhite;
4943 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4944 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4945 CGRect indrect = {{indoffset, indoffset}, indsize};
4947 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4948 [indicator_ setStyle:style];
4949 [overlay_ addSubview:indicator_];
4951 CGSize prmsize = {215, indsize.height + 4};
4954 indoffset * 2 + indsize.width,
4958 unsigned(ovrrect.size.height - prmsize.height) / 2
4961 UIFont *font = [UIFont systemFontOfSize:15];
4963 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4965 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
4966 [prompt_ setBackgroundColor:[UIColor clearColor]];
4967 [prompt_ setFont:font];
4969 [overlay_ addSubview:prompt_];
4971 CGSize prgsize = {75, 100};
4974 ovrrect.size.width - prgsize.width - 10,
4975 (ovrrect.size.height - prgsize.height) / 2
4978 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4979 [progress_ setStyle:0];
4980 [overlay_ addSubview:progress_];
4982 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
4983 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
4985 CGRect frame = [cancel_ frame];
4986 frame.size.width = 65;
4987 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
4988 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
4989 [cancel_ setFrame:frame];
4991 [cancel_ setBarStyle:barstyle];
4995 - (void) _onCancel {
4997 [cancel_ removeFromSuperview];
5000 - (void) _update { _pooled
5002 status.setDelegate(self);
5004 [database_ updateWithStatus:status];
5007 performSelectorOnMainThread:@selector(_update_)
5013 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5014 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5017 - (void) setProgressTitle:(NSString *)title {
5019 performSelectorOnMainThread:@selector(_setProgressTitle:)
5025 - (void) setProgressPercent:(float)percent {
5027 performSelectorOnMainThread:@selector(_setProgressPercent:)
5028 withObject:[NSNumber numberWithFloat:percent]
5033 - (void) startProgress {
5036 - (void) addProgressOutput:(NSString *)output {
5038 performSelectorOnMainThread:@selector(_addProgressOutput:)
5044 - (bool) isCancelling:(size_t)received {
5045 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5046 if (received_ != received) {
5047 received_ = received;
5049 } else if (now - last_ > 15)
5054 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5058 - (void) _setProgressTitle:(NSString *)title {
5059 [prompt_ setText:title];
5062 - (void) _setProgressPercent:(NSNumber *)percent {
5063 [progress_ setProgress:[percent floatValue]];
5066 - (void) _addProgressOutput:(NSString *)output {
5071 /* Cydia:// Protocol {{{ */
5072 @interface CydiaURLProtocol : NSURLProtocol {
5077 @implementation CydiaURLProtocol
5079 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5080 NSURL *url([request URL]);
5083 NSString *scheme([[url scheme] lowercaseString]);
5084 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5089 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5093 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5094 id<NSURLProtocolClient> client([self client]);
5095 NSData *data(UIImagePNGRepresentation(icon));
5097 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5098 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5099 [client URLProtocol:self didLoadData:data];
5100 [client URLProtocolDidFinishLoading:self];
5103 - (void) startLoading {
5104 id<NSURLProtocolClient> client([self client]);
5105 NSURLRequest *request([self request]);
5107 NSURL *url([request URL]);
5108 NSString *href([url absoluteString]);
5110 NSString *path([href substringFromIndex:8]);
5111 NSRange slash([path rangeOfString:@"/"]);
5114 if (slash.location == NSNotFound) {
5118 command = [path substringToIndex:slash.location];
5119 path = [path substringFromIndex:(slash.location + 1)];
5122 Database *database([Database sharedInstance]);
5124 if ([command isEqualToString:@"package-icon"]) {
5127 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5128 Package *package([database packageWithName:path]);
5131 UIImage *icon([package icon]);
5132 [self _returnPNGWithImage:icon forRequest:request];
5133 } else if ([command isEqualToString:@"source-icon"]) {
5136 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5137 NSString *source(Simplify(path));
5138 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5140 icon = [UIImage applicationImageNamed:@"unknown.png"];
5141 [self _returnPNGWithImage:icon forRequest:request];
5142 } else if ([command isEqualToString:@"uikit-image"]) {
5145 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5146 UIImage *icon(_UIImageWithName(path));
5147 [self _returnPNGWithImage:icon forRequest:request];
5148 } else if ([command isEqualToString:@"section-icon"]) {
5151 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5152 NSString *section(Simplify(path));
5153 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5155 icon = [UIImage applicationImageNamed:@"unknown.png"];
5156 [self _returnPNGWithImage:icon forRequest:request];
5158 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5162 - (void) stopLoading {
5168 /* Install View {{{ */
5169 @interface InstallView : RVPage {
5170 _transient Database *database_;
5171 NSMutableArray *sections_;
5172 NSMutableArray *filtered_;
5173 UITransitionView *transition_;
5179 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5180 - (void) reloadData;
5185 @implementation InstallView
5188 [list_ setDataSource:nil];
5189 [list_ setDelegate:nil];
5191 [sections_ release];
5192 [filtered_ release];
5193 [transition_ release];
5195 [accessory_ release];
5199 - (int) numberOfRowsInTable:(UITable *)table {
5200 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5203 - (float) table:(UITable *)table heightForRow:(int)row {
5207 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5209 reusing = [[[SectionCell alloc] init] autorelease];
5210 [(SectionCell *)reusing setSection:(editing_ ?
5211 [sections_ objectAtIndex:row] :
5212 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5213 ) editing:editing_];
5217 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5221 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5225 - (void) tableRowSelected:(NSNotification *)notification {
5226 int row = [[notification object] selectedRow];
5237 title = @"All Packages";
5239 section = [filtered_ objectAtIndex:(row - 1)];
5240 name = [section name];
5246 title = @"(No Section)";
5250 PackageTable *table = [[[PackageTable alloc]
5254 filter:@selector(isVisiblyUninstalledInSection:)
5258 [table setDelegate:delegate_];
5260 [book_ pushPage:table];
5263 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5264 if ((self = [super initWithBook:book]) != nil) {
5265 database_ = database;
5267 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5268 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5270 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5271 [self addSubview:transition_];
5273 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5274 [transition_ transition:0 toView:list_];
5276 UITableColumn *column = [[[UITableColumn alloc]
5277 initWithTitle:@"Name"
5279 width:[self frame].size.width
5282 [list_ setDataSource:self];
5283 [list_ setSeparatorStyle:1];
5284 [list_ addTableColumn:column];
5285 [list_ setDelegate:self];
5286 [list_ setReusesTableCells:YES];
5290 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5291 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5295 - (void) reloadData {
5296 NSArray *packages = [database_ packages];
5298 [sections_ removeAllObjects];
5299 [filtered_ removeAllObjects];
5301 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5302 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5305 for (size_t i(0); i != [packages count]; ++i) {
5306 Package *package([packages objectAtIndex:i]);
5307 NSString *name([package section]);
5310 Section *section([sections objectForKey:name]);
5311 if (section == nil) {
5312 section = [[[Section alloc] initWithName:name] autorelease];
5313 [sections setObject:section forKey:name];
5317 if ([package valid] && [package installed] == nil && [package visible])
5318 [filtered addObject:package];
5322 [sections_ addObjectsFromArray:[sections allValues]];
5323 [sections_ sortUsingSelector:@selector(compareByName:)];
5326 [filtered sortUsingSelector:@selector(compareBySection:)];
5329 Section *section = nil;
5330 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5331 Package *package = [filtered objectAtIndex:offset];
5332 NSString *name = [package section];
5334 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5335 section = name == nil ?
5336 [[[Section alloc] initWithName:nil] autorelease] :
5337 [sections objectForKey:name];
5338 [filtered_ addObject:section];
5341 [section addToCount];
5349 - (void) resetView {
5351 [self _rightButtonClicked];
5354 - (void) resetViewAnimated:(BOOL)animated {
5355 [list_ resetViewAnimated:animated];
5358 - (void) _rightButtonClicked {
5359 if ((editing_ = !editing_))
5362 [delegate_ updateData];
5363 [book_ reloadTitleForPage:self];
5364 [book_ reloadButtonsForPage:self];
5367 - (NSString *) title {
5368 return editing_ ? @"Section Visibility" : @"Install by Section";
5371 - (NSString *) backButtonTitle {
5375 - (id) rightButtonTitle {
5376 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5379 - (UINavigationButtonStyle) rightButtonStyle {
5380 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5383 - (UIView *) accessoryView {
5389 /* Changes View {{{ */
5390 @interface ChangesView : RVPage {
5391 _transient Database *database_;
5392 NSMutableArray *packages_;
5393 NSMutableArray *sections_;
5394 UISectionList *list_;
5398 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5399 - (void) reloadData;
5403 @implementation ChangesView
5406 [[list_ table] setDelegate:nil];
5407 [list_ setDataSource:nil];
5409 [packages_ release];
5410 [sections_ release];
5415 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5416 return [sections_ count];
5419 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5420 return [[sections_ objectAtIndex:section] name];
5423 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5424 return [[sections_ objectAtIndex:section] row];
5427 - (int) numberOfRowsInTable:(UITable *)table {
5428 return [packages_ count];
5431 - (float) table:(UITable *)table heightForRow:(int)row {
5432 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5435 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5437 reusing = [[[PackageCell alloc] init] autorelease];
5438 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5442 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5446 - (void) tableRowSelected:(NSNotification *)notification {
5447 int row = [[notification object] selectedRow];
5450 Package *package = [packages_ objectAtIndex:row];
5451 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5452 [view setDelegate:delegate_];
5453 [view setPackage:package];
5454 [book_ pushPage:view];
5457 - (void) _leftButtonClicked {
5458 [(CYBook *)book_ update];
5459 [self reloadButtons];
5462 - (void) _rightButtonClicked {
5463 [delegate_ distUpgrade];
5466 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5467 if ((self = [super initWithBook:book]) != nil) {
5468 database_ = database;
5470 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5471 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5473 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5474 [self addSubview:list_];
5476 [list_ setShouldHideHeaderInShortLists:NO];
5477 [list_ setDataSource:self];
5478 //[list_ setSectionListStyle:1];
5480 UITableColumn *column = [[[UITableColumn alloc]
5481 initWithTitle:@"Name"
5483 width:[self frame].size.width
5486 UITable *table = [list_ table];
5487 [table setSeparatorStyle:1];
5488 [table addTableColumn:column];
5489 [table setDelegate:self];
5490 [table setReusesTableCells:YES];
5494 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5495 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5499 - (void) reloadData {
5500 NSArray *packages = [database_ packages];
5502 [packages_ removeAllObjects];
5503 [sections_ removeAllObjects];
5506 for (size_t i(0); i != [packages count]; ++i) {
5507 Package *package([packages objectAtIndex:i]);
5510 [package installed] == nil && [package valid] && [package visible] ||
5511 [package upgradableAndEssential:NO]
5513 [packages_ addObject:package];
5517 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5520 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5521 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5522 Section *section = nil;
5526 bool unseens = false;
5528 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5531 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5532 Package *package = [packages_ objectAtIndex:offset];
5534 if (![package upgradableAndEssential:YES]) {
5536 NSDate *seen = [package seen];
5538 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5541 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5542 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5543 [sections_ addObject:section];
5547 [section addToCount];
5548 } else if ([package ignored])
5549 [ignored addToCount];
5552 [upgradable addToCount];
5557 CFRelease(formatter);
5560 Section *last = [sections_ lastObject];
5561 size_t count = [last count];
5562 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5563 [sections_ removeLastObject];
5566 if ([ignored count] != 0)
5567 [sections_ insertObject:ignored atIndex:0];
5569 [sections_ insertObject:upgradable atIndex:0];
5572 [self reloadButtons];
5575 - (void) resetViewAnimated:(BOOL)animated {
5576 [list_ resetViewAnimated:animated];
5579 - (NSString *) leftButtonTitle {
5580 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5583 - (id) rightButtonTitle {
5584 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5587 - (NSString *) title {
5593 /* Search View {{{ */
5594 @protocol SearchViewDelegate
5595 - (void) showKeyboard:(BOOL)show;
5598 @interface SearchView : RVPage {
5600 UISearchField *field_;
5601 UITransitionView *transition_;
5602 PackageTable *table_;
5603 UIPreferencesTable *advanced_;
5609 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5610 - (void) reloadData;
5614 @implementation SearchView
5617 [field_ setDelegate:nil];
5619 [accessory_ release];
5621 [transition_ release];
5623 [advanced_ release];
5628 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5632 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5634 case 0: return @"Advanced Search (Coming Soon!)";
5636 default: _assert(false);
5640 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5644 default: _assert(false);
5648 - (void) _showKeyboard:(BOOL)show {
5649 CGSize keysize = [UIKeyboard defaultSize];
5650 CGRect keydown = [book_ pageBounds];
5651 CGRect keyup = keydown;
5652 keyup.size.height -= keysize.height - ButtonBarHeight_;
5654 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5656 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5657 [animation setSignificantRectFields:8];
5660 [animation setStartFrame:keydown];
5661 [animation setEndFrame:keyup];
5663 [animation setStartFrame:keyup];
5664 [animation setEndFrame:keydown];
5667 UIAnimator *animator = [UIAnimator sharedAnimator];
5670 addAnimations:[NSArray arrayWithObjects:animation, nil]
5671 withDuration:(KeyboardTime_ - delay)
5676 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5678 [delegate_ showKeyboard:show];
5681 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5682 [self _showKeyboard:YES];
5685 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5686 [self _showKeyboard:NO];
5689 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5691 NSString *text([field_ text]);
5692 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5698 - (void) textFieldClearButtonPressed:(UITextField *)field {
5702 - (void) keyboardInputShouldDelete:(id)input {
5706 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5707 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5711 [field_ resignFirstResponder];
5716 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5717 if ((self = [super initWithBook:book]) != nil) {
5718 CGRect pageBounds = [book_ pageBounds];
5720 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5721 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5722 [self addSubview:pinstripe];*/
5724 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5725 [self addSubview:transition_];
5727 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5729 [advanced_ setReusesTableCells:YES];
5730 [advanced_ setDataSource:self];
5731 [advanced_ reloadData];
5733 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5734 CGColor dimmed(space_, 0, 0, 0, 0.5);
5735 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5737 table_ = [[PackageTable alloc]
5741 filter:@selector(isUnfilteredAndSearchedForBy:)
5745 [table_ setShouldHideHeaderInShortLists:NO];
5746 [transition_ transition:0 toView:table_];
5755 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5762 [self bounds].size.width - area.origin.x - 18;
5764 area.size.height = [UISearchField defaultHeight];
5766 field_ = [[UISearchField alloc] initWithFrame:area];
5768 UIFont *font = [UIFont systemFontOfSize:16];
5769 [field_ setFont:font];
5771 [field_ setPlaceholder:@"Package Names & Descriptions"];
5772 [field_ setDelegate:self];
5774 [field_ setPaddingTop:5];
5776 UITextInputTraits *traits([field_ textInputTraits]);
5777 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5778 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5779 [traits setReturnKeyType:UIReturnKeySearch];
5781 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5783 accessory_ = [[UIView alloc] initWithFrame:accrect];
5784 [accessory_ addSubview:field_];
5786 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5787 [configure setShowPressFeedback:YES];
5788 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5789 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5790 [accessory_ addSubview:configure];*/
5792 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5793 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5799 LKAnimation *animation = [LKTransition animation];
5800 [animation setType:@"oglFlip"];
5801 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5802 [animation setFillMode:@"extended"];
5803 [animation setTransitionFlags:3];
5804 [animation setDuration:10];
5805 [animation setSpeed:0.35];
5806 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5807 [[transition_ _layer] addAnimation:animation forKey:0];
5808 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5809 flipped_ = !flipped_;
5813 - (void) configurePushed {
5814 [field_ resignFirstResponder];
5818 - (void) resetViewAnimated:(BOOL)animated {
5821 [table_ resetViewAnimated:animated];
5824 - (void) _reloadData {
5827 - (void) reloadData {
5830 [table_ setObject:[field_ text]];
5831 [table_ reloadData];
5832 [table_ resetCursor];
5835 - (UIView *) accessoryView {
5839 - (NSString *) title {
5843 - (NSString *) backButtonTitle {
5847 - (void) setDelegate:(id)delegate {
5848 [table_ setDelegate:delegate];
5849 [super setDelegate:delegate];
5855 @interface SettingsView : RVPage {
5856 _transient Database *database_;
5859 UIPreferencesTable *table_;
5860 _UISwitchSlider *subscribedSwitch_;
5861 _UISwitchSlider *ignoredSwitch_;
5862 UIPreferencesControlTableCell *subscribedCell_;
5863 UIPreferencesControlTableCell *ignoredCell_;
5866 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5870 @implementation SettingsView
5873 [table_ setDataSource:nil];
5876 if (package_ != nil)
5879 [subscribedSwitch_ release];
5880 [ignoredSwitch_ release];
5881 [subscribedCell_ release];
5882 [ignoredCell_ release];
5886 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5887 if (package_ == nil)
5893 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5894 if (package_ == nil)
5901 default: _assert(false);
5907 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5908 if (package_ == nil)
5915 default: _assert(false);
5921 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5922 if (package_ == nil)
5929 default: _assert(false);
5935 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5936 if (package_ == nil)
5939 _UISwitchSlider *slider([cell control]);
5940 BOOL value([slider value] != 0);
5941 NSMutableDictionary *metadata([package_ metadata]);
5944 if (NSNumber *number = [metadata objectForKey:key])
5945 before = [number boolValue];
5949 if (value != before) {
5950 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5952 [delegate_ updateData];
5956 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5957 [self onSomething:cell withKey:@"IsSubscribed"];
5960 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5961 [self onSomething:cell withKey:@"IsIgnored"];
5964 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5965 if (package_ == nil)
5969 case 0: switch (row) {
5971 return subscribedCell_;
5973 return ignoredCell_;
5974 default: _assert(false);
5977 case 1: switch (row) {
5979 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
5980 [cell setShowSelection:NO];
5981 [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."];
5985 default: _assert(false);
5988 default: _assert(false);
5994 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
5995 if ((self = [super initWithBook:book])) {
5996 database_ = database;
5997 name_ = [package retain];
5999 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6000 [self addSubview:table_];
6002 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6003 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6005 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6006 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6008 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6009 [subscribedCell_ setShowSelection:NO];
6010 [subscribedCell_ setTitle:@"Show All Changes"];
6011 [subscribedCell_ setControl:subscribedSwitch_];
6013 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6014 [ignoredCell_ setShowSelection:NO];
6015 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6016 [ignoredCell_ setControl:ignoredSwitch_];
6018 [table_ setDataSource:self];
6023 - (void) resetViewAnimated:(BOOL)animated {
6024 [table_ resetViewAnimated:animated];
6027 - (void) reloadData {
6028 if (package_ != nil)
6029 [package_ autorelease];
6030 package_ = [database_ packageWithName:name_];
6031 if (package_ != nil) {
6033 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6034 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6037 [table_ reloadData];
6040 - (NSString *) title {
6046 /* Signature View {{{ */
6047 @interface SignatureView : BrowserView {
6048 _transient Database *database_;
6052 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6056 @implementation SignatureView
6063 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6065 [super webView:sender didClearWindowObject:window forFrame:frame];
6068 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6069 if ((self = [super initWithBook:book]) != nil) {
6070 database_ = database;
6071 package_ = [package retain];
6076 - (void) resetViewAnimated:(BOOL)animated {
6079 - (void) reloadData {
6080 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6086 @interface Cydia : UIApplication <
6087 ConfirmationViewDelegate,
6088 ProgressViewDelegate,
6097 UIToolbar *buttonbar_;
6101 NSMutableArray *essential_;
6102 NSMutableArray *broken_;
6104 Database *database_;
6105 ProgressView *progress_;
6109 UIKeyboard *keyboard_;
6110 UIProgressHUD *hud_;
6112 InstallView *install_;
6113 ChangesView *changes_;
6114 ManageView *manage_;
6115 SearchView *search_;
6120 @implementation Cydia
6123 if ([broken_ count] != 0) {
6124 int count = [broken_ count];
6126 UIActionSheet *sheet = [[[UIActionSheet alloc]
6127 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6128 buttons:[NSArray arrayWithObjects:
6130 @"Ignore (Temporary)",
6132 defaultButtonIndex:0
6137 [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."];
6138 [sheet popupAlertAnimated:YES];
6139 } else if (!Ignored_ && [essential_ count] != 0) {
6140 int count = [essential_ count];
6142 UIActionSheet *sheet = [[[UIActionSheet alloc]
6143 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6144 buttons:[NSArray arrayWithObjects:
6145 @"Upgrade Essential",
6146 @"Complete Upgrade",
6147 @"Ignore (Temporary)",
6149 defaultButtonIndex:0
6154 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6155 [sheet popupAlertAnimated:YES];
6159 - (void) _reloadData {
6160 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6161 [hud setText:@"Reloading Data"];
6162 [overlay_ addSubview:hud];
6165 [database_ reloadData];
6169 [essential_ removeAllObjects];
6170 [broken_ removeAllObjects];
6172 NSArray *packages = [database_ packages];
6173 for (Package *package in packages) {
6175 [broken_ addObject:package];
6176 if ([package upgradableAndEssential:NO]) {
6177 if ([package essential])
6178 [essential_ addObject:package];
6184 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6185 [buttonbar_ setBadgeValue:badge forButton:3];
6186 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6187 [buttonbar_ setBadgeAnimated:YES forButton:3];
6188 [self setApplicationBadge:badge];
6190 [buttonbar_ setBadgeValue:nil forButton:3];
6191 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6192 [buttonbar_ setBadgeAnimated:NO forButton:3];
6193 [self removeApplicationBadge];
6199 if ([packages count] == 0);
6211 [hud removeFromSuperview];*/
6214 - (void) _saveConfig {
6217 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6223 - (void) updateData {
6226 /* XXX: this is just stupid */
6227 if (tag_ != 2 && install_ != nil)
6228 [install_ reloadData];
6229 if (tag_ != 3 && changes_ != nil)
6230 [changes_ reloadData];
6231 if (tag_ != 5 && search_ != nil)
6232 [search_ reloadData];
6242 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6243 _assert(file != NULL);
6245 NSArray *keys = [Sources_ allKeys];
6247 for (int i(0), e([keys count]); i != e; ++i) {
6248 NSString *key = [keys objectAtIndex:i];
6249 NSDictionary *source = [Sources_ objectForKey:key];
6251 fprintf(file, "%s %s %s\n",
6252 [[source objectForKey:@"Type"] UTF8String],
6253 [[source objectForKey:@"URI"] UTF8String],
6254 [[source objectForKey:@"Distribution"] UTF8String]
6263 detachNewThreadSelector:@selector(update_)
6266 title:@"Updating Sources"
6270 - (void) reloadData {
6271 @synchronized (self) {
6272 if (confirm_ == nil)
6278 pkgProblemResolver *resolver = [database_ resolver];
6280 resolver->InstallProtect();
6281 if (!resolver->Resolve(true))
6286 [database_ prepare];
6288 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6289 [confirm_ setDelegate:self];
6291 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6292 [page setDelegate:self];
6294 [confirm_ setPage:page];
6295 [underlay_ popSubview:confirm_];
6298 - (void) installPackage:(Package *)package {
6299 @synchronized (self) {
6306 - (void) removePackage:(Package *)package {
6307 @synchronized (self) {
6314 - (void) distUpgrade {
6315 @synchronized (self) {
6316 [database_ upgrade];
6322 @synchronized (self) {
6324 if (confirm_ != nil) {
6332 [overlay_ removeFromSuperview];
6336 detachNewThreadSelector:@selector(perform)
6343 - (void) bootstrap_ {
6345 [database_ upgrade];
6346 [database_ prepare];
6347 [database_ perform];
6350 - (void) bootstrap {
6352 detachNewThreadSelector:@selector(bootstrap_)
6355 title:@"Bootstrap Install"
6359 - (void) progressViewIsComplete:(ProgressView *)progress {
6360 if (confirm_ != nil) {
6361 [underlay_ addSubview:overlay_];
6362 [confirm_ popFromSuperviewAnimated:NO];
6368 - (void) setPage:(RVPage *)page {
6369 [page resetViewAnimated:NO];
6370 [page setDelegate:self];
6371 [book_ setPage:page];
6374 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6375 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6376 [browser loadURL:url];
6380 - (void) _setHomePage {
6381 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6384 - (void) buttonBarItemTapped:(id)sender {
6385 unsigned tag = [sender tag];
6387 [book_ resetViewAnimated:YES];
6389 } else if (tag_ == 2 && tag != 2)
6390 [install_ resetView];
6393 case 1: [self _setHomePage]; break;
6395 case 2: [self setPage:install_]; break;
6396 case 3: [self setPage:changes_]; break;
6397 case 4: [self setPage:manage_]; break;
6398 case 5: [self setPage:search_]; break;
6400 default: _assert(false);
6406 - (void) applicationWillSuspend {
6408 [super applicationWillSuspend];
6411 - (void) askForSettings {
6412 UIActionSheet *role = [[[UIActionSheet alloc]
6413 initWithTitle:@"Who Are You?"
6414 buttons:[NSArray arrayWithObjects:
6415 @"User (Graphical Only)",
6416 @"Hacker (+ Command Line)",
6417 @"Developer (No Filters)",
6419 defaultButtonIndex:-1
6424 [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."];
6425 [role popupAlertAnimated:YES];
6430 [self setStatusBarShowsProgress:NO];
6433 [hud_ removeFromSuperview];
6437 pid_t pid = ExecFork();
6439 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6440 perror("launchctl stop");
6447 [self askForSettings];
6451 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6453 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6454 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6455 0, 0, screenrect.size.width, screenrect.size.height - 48
6456 ) database:database_];
6458 [book_ setDelegate:self];
6460 [overlay_ addSubview:book_];
6462 NSArray *buttonitems = [NSArray arrayWithObjects:
6463 [NSDictionary dictionaryWithObjectsAndKeys:
6464 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6465 @"home-up.png", kUIButtonBarButtonInfo,
6466 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6467 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6468 self, kUIButtonBarButtonTarget,
6469 @"Home", kUIButtonBarButtonTitle,
6470 @"0", kUIButtonBarButtonType,
6473 [NSDictionary dictionaryWithObjectsAndKeys:
6474 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6475 @"install-up.png", kUIButtonBarButtonInfo,
6476 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6477 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6478 self, kUIButtonBarButtonTarget,
6479 @"Sections", kUIButtonBarButtonTitle,
6480 @"0", kUIButtonBarButtonType,
6483 [NSDictionary dictionaryWithObjectsAndKeys:
6484 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6485 @"changes-up.png", kUIButtonBarButtonInfo,
6486 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6487 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6488 self, kUIButtonBarButtonTarget,
6489 @"Changes", kUIButtonBarButtonTitle,
6490 @"0", kUIButtonBarButtonType,
6493 [NSDictionary dictionaryWithObjectsAndKeys:
6494 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6495 @"manage-up.png", kUIButtonBarButtonInfo,
6496 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6497 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6498 self, kUIButtonBarButtonTarget,
6499 @"Manage", kUIButtonBarButtonTitle,
6500 @"0", kUIButtonBarButtonType,
6503 [NSDictionary dictionaryWithObjectsAndKeys:
6504 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6505 @"search-up.png", kUIButtonBarButtonInfo,
6506 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6507 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6508 self, kUIButtonBarButtonTarget,
6509 @"Search", kUIButtonBarButtonTitle,
6510 @"0", kUIButtonBarButtonType,
6514 buttonbar_ = [[UIToolbar alloc]
6516 withFrame:CGRectMake(
6517 0, screenrect.size.height - ButtonBarHeight_,
6518 screenrect.size.width, ButtonBarHeight_
6520 withItemList:buttonitems
6523 [buttonbar_ setDelegate:self];
6524 [buttonbar_ setBarStyle:1];
6525 [buttonbar_ setButtonBarTrackingMode:2];
6527 int buttons[5] = {1, 2, 3, 4, 5};
6528 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6529 [buttonbar_ showButtonGroup:0 withDuration:0];
6531 for (int i = 0; i != 5; ++i)
6532 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6533 i * 64 + 2, 1, 60, ButtonBarHeight_
6536 [buttonbar_ showSelectionForButton:1];
6537 [overlay_ addSubview:buttonbar_];
6539 [UIKeyboard initImplementationNow];
6540 CGSize keysize = [UIKeyboard defaultSize];
6541 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6542 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6543 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6544 [overlay_ addSubview:keyboard_];
6547 [underlay_ addSubview:overlay_];
6551 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6552 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6553 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6555 manage_ = (ManageView *) [[self
6556 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6557 withClass:[ManageView class]
6563 [self _setHomePage];
6566 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6567 NSString *context([sheet context]);
6569 if ([context isEqualToString:@"fixhalf"]) {
6572 @synchronized (self) {
6573 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6574 Package *broken = [broken_ objectAtIndex:i];
6577 NSString *id = [broken id];
6578 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6579 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6580 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6581 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6590 [broken_ removeAllObjects];
6599 } else if ([context isEqualToString:@"role"]) {
6601 case 1: Role_ = @"User"; break;
6602 case 2: Role_ = @"Hacker"; break;
6603 case 3: Role_ = @"Developer"; break;
6610 bool reset = Settings_ != nil;
6612 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6616 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6626 } else if ([context isEqualToString:@"upgrade"]) {
6629 @synchronized (self) {
6630 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6631 Package *essential = [essential_ objectAtIndex:i];
6632 [essential install];
6656 - (void) reorganize { _pooled
6657 system("/usr/libexec/cydia/free.sh");
6658 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6661 - (void) applicationSuspend:(__GSEvent *)event {
6662 if (hud_ == nil && ![progress_ isRunning])
6663 [super applicationSuspend:event];
6666 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6668 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6671 - (void) _setSuspended:(BOOL)value {
6673 [super _setSuspended:value];
6676 - (UIProgressHUD *) addProgressHUD {
6677 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6679 [underlay_ addSubview:hud];
6683 - (void) openMailToURL:(NSURL *)url {
6684 // XXX: this makes me sad
6686 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6688 [UIApp openURL:url];// asPanel:YES];
6692 - (void) clearFirstResponder {
6693 if (id responder = [window_ firstResponder])
6694 [responder resignFirstResponder];
6697 - (RVPage *) pageForPackage:(NSString *)name {
6698 if (Package *package = [database_ packageWithName:name]) {
6699 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6700 [view setPackage:package];
6703 UIActionSheet *sheet = [[[UIActionSheet alloc]
6704 initWithTitle:@"Cannot Locate Package"
6705 buttons:[NSArray arrayWithObjects:@"Close", nil]
6706 defaultButtonIndex:0
6711 [sheet setBodyText:[NSString stringWithFormat:
6712 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6715 [sheet popupAlertAnimated:YES];
6720 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6721 NSString *href = [url absoluteString];
6726 if ([href isEqualToString:@"cydia://add-source"])
6727 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6728 else if ([href isEqualToString:@"cydia://sources"])
6729 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6730 else if ([href isEqualToString:@"cydia://packages"])
6731 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6732 else if ([href hasPrefix:@"cydia://url/"])
6733 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6734 else if ([href hasPrefix:@"cydia://launch/"])
6735 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6736 else if ([href hasPrefix:@"cydia://package-settings/"])
6737 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
6738 else if ([href hasPrefix:@"cydia://package-signature/"])
6739 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6740 else if ([href hasPrefix:@"cydia://package/"])
6741 return [self pageForPackage:[href substringFromIndex:16]];
6742 else if ([href hasPrefix:@"cydia://files/"]) {
6743 NSString *name = [href substringFromIndex:14];
6745 if (Package *package = [database_ packageWithName:name]) {
6746 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6747 [files setPackage:package];
6755 - (void) applicationOpenURL:(NSURL *)url {
6756 [super applicationOpenURL:url];
6758 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6759 [self setPage:page];
6760 [buttonbar_ showSelectionForButton:tag];
6765 - (void) applicationDidFinishLaunching:(id)unused {
6766 Font12_ = [[UIFont systemFontOfSize:12] retain];
6767 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6768 Font14_ = [[UIFont systemFontOfSize:14] retain];
6769 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6770 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6772 _assert(pkgInitConfig(*_config));
6773 _assert(pkgInitSystem(*_config, _system));
6777 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6778 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6780 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6782 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6783 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6785 [window_ orderFront:self];
6786 [window_ makeKey:self];
6787 [window_ setHidden:NO];
6789 database_ = [Database sharedInstance];
6790 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6791 [database_ setDelegate:progress_];
6792 [window_ setContentView:progress_];
6794 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6795 [progress_ setContentView:underlay_];
6797 [progress_ resetView];
6800 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6801 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6802 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6803 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6804 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6805 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6806 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6808 [self setIdleTimerDisabled:YES];
6810 hud_ = [self addProgressHUD];
6811 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6813 [self setStatusBarShowsProgress:YES];
6816 detachNewThreadSelector:@selector(reorganize)
6824 - (void) showKeyboard:(BOOL)show {
6825 CGSize keysize = [UIKeyboard defaultSize];
6826 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6827 CGRect keyup = keydown;
6828 keyup.origin.y -= keysize.height;
6830 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6831 [animation setSignificantRectFields:2];
6834 [animation setStartFrame:keydown];
6835 [animation setEndFrame:keyup];
6836 [keyboard_ activate];
6838 [animation setStartFrame:keyup];
6839 [animation setEndFrame:keydown];
6840 [keyboard_ deactivate];
6843 [[UIAnimator sharedAnimator]
6844 addAnimations:[NSArray arrayWithObjects:animation, nil]
6845 withDuration:KeyboardTime_
6850 - (void) slideUp:(UIActionSheet *)alert {
6852 [alert presentSheetFromButtonBar:buttonbar_];
6854 [alert presentSheetInView:overlay_];
6859 void AddPreferences(NSString *plist) { _pooled
6860 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6861 _assert(settings != NULL);
6862 NSMutableArray *items = [settings objectForKey:@"items"];
6866 for (size_t i(0); i != [items count]; ++i) {
6867 NSMutableDictionary *item([items objectAtIndex:i]);
6868 NSString *label = [item objectForKey:@"label"];
6869 if (label != nil && [label isEqualToString:@"Cydia"]) {
6876 for (size_t i(0); i != [items count]; ++i) {
6877 NSDictionary *item([items objectAtIndex:i]);
6878 NSString *label = [item objectForKey:@"label"];
6879 if (label != nil && [label isEqualToString:@"General"]) {
6880 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6881 @"CydiaSettings", @"bundle",
6882 @"PSLinkCell", @"cell",
6883 [NSNumber numberWithBool:YES], @"hasIcon",
6884 [NSNumber numberWithBool:YES], @"isController",
6886 nil] atIndex:(i + 1)];
6892 _assert([settings writeToFile:plist atomically:YES] == YES);
6897 id Alloc_(id self, SEL selector) {
6898 id object = alloc_(self, selector);
6899 lprintf("[%s]A-%p\n", self->isa->name, object);
6904 id Dealloc_(id self, SEL selector) {
6905 id object = dealloc_(self, selector);
6906 lprintf("[%s]D-%p\n", self->isa->name, object);
6910 int main(int argc, char *argv[]) { _pooled
6911 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6913 bool substrate(false);
6919 for (int argi(1); argi != argc; ++argi)
6920 if (strcmp(argv[argi], "--") == 0) {
6922 argv[argi] = argv[0];
6928 for (int argi(1); argi != arge; ++argi)
6929 if (strcmp(args[argi], "--bootstrap") == 0)
6931 else if (strcmp(args[argi], "--substrate") == 0)
6934 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6937 App_ = [[NSBundle mainBundle] bundlePath];
6938 Home_ = NSHomeDirectory();
6939 Locale_ = CFLocaleCopyCurrent();
6942 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6943 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6944 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6945 Sounds_Keyboard_ = [keyboard boolValue];
6951 #if 1 /* XXX: this costs 1.4s of startup performance */
6952 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6953 _assert(errno == ENOENT);
6954 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6955 _assert(errno == ENOENT);
6958 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6959 alloc_ = alloc->method_imp;
6960 alloc->method_imp = (IMP) &Alloc_;*/
6962 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6963 dealloc_ = dealloc->method_imp;
6964 dealloc->method_imp = (IMP) &Dealloc_;*/
6969 size = sizeof(maxproc);
6970 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6971 perror("sysctlbyname(\"kern.maxproc\", ?)");
6972 else if (maxproc < 64) {
6974 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6975 perror("sysctlbyname(\"kern.maxproc\", #)");
6978 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6979 char *machine = new char[size];
6980 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6981 perror("sysctlbyname(\"hw.machine\", ?)");
6985 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6987 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6988 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6990 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
6991 Indices_ = [[NSMutableDictionary alloc] init];*/
6993 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6994 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@P", @"Rating",
6997 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6998 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7000 Settings_ = [Metadata_ objectForKey:@"Settings"];
7002 Packages_ = [Metadata_ objectForKey:@"Packages"];
7003 Sections_ = [Metadata_ objectForKey:@"Sections"];
7004 Sources_ = [Metadata_ objectForKey:@"Sources"];
7007 if (Settings_ != nil)
7008 Role_ = [Settings_ objectForKey:@"Role"];
7010 if (Packages_ == nil) {
7011 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7012 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7015 if (Sections_ == nil) {
7016 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7017 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7020 if (Sources_ == nil) {
7021 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7022 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7026 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7029 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7030 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7032 if (access("/User", F_OK) != 0)
7033 system("/usr/libexec/cydia/firmware.sh");
7035 _assert([[NSFileManager defaultManager]
7036 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7037 withIntermediateDirectories:YES
7042 space_ = CGColorSpaceCreateDeviceRGB();
7044 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7045 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7046 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7047 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7048 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7049 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7051 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7053 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7055 UIApplicationUseLegacyEvents(YES);
7056 UIKeyboardDisableAutomaticAppearance();
7058 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7060 CGColorSpaceRelease(space_);