1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #include <objc/objc.h>
40 #include <objc/runtime.h>
42 #include <CoreGraphics/CoreGraphics.h>
43 #include <GraphicsServices/GraphicsServices.h>
44 #include <Foundation/Foundation.h>
46 #include <WebCore/DOMHTML.h>
47 #import <QuartzCore/CALayer.h>
49 #import <UIKit/UIActionSheet.h>
50 #import <UIKit/UIAnimator.h>
51 #import <UIKit/UIApplication.h>
52 #import <UIKit/UIColor.h>
53 #import <UIKit/UIFieldEditor.h>
54 #import <UIKit/UIFrameAnimation.h>
55 #import <UIKit/UIHardware.h>
56 #import <UIKit/UIImage.h>
57 #import <UIKit/UIImageAndTextTableCell.h>
58 #import <UIKit/UIImageView.h>
59 #import <UIKit/UIKeyboard.h>
60 #import <UIKit/UIKeyboardImpl.h>
61 #import <UIKit/UINavigationBar.h>
62 #import <UIKit/UINavigationItem.h>
63 #import <UIKit/UIPreferencesTable.h>
64 #import <UIKit/UIPreferencesTableCell.h>
65 #import <UIKit/UIProgressBar.h>
66 #import <UIKit/UIProgressHUD.h>
67 #import <UIKit/UIProgressIndicator.h>
68 #import <UIKit/UIPushButton.h>
69 #import <UIKit/UISearchField.h>
70 #import <UIKit/UISimpleTableCell.h>
71 #import <UIKit/_UISwitchSlider.h>
72 #import <UIKit/UITableCell.h>
73 #import <UIKit/UITableColumn.h>
74 #import <UIKit/UITextField.h>
75 #import <UIKit/UITextInputTraits.h>
76 #import <UIKit/UITextLabel.h>
77 #import <UIKit/UITextView.h>
78 #import <UIKit/UIToolbar.h>
79 #import <UIKit/UITransitionView.h>
80 #import <UIKit/UIWebDocumentView.h>
81 #import <UIKit/UIWebView.h>
82 #import <UIKit/UIWindow.h>
84 #import <UIKit/UIView-Geometry.h>
85 #import <UIKit/UIView-Gestures.h>
86 #import <UIKit/UIView-Hierarchy.h>
87 #import <UIKit/UIView-Rendering.h>
89 #import <UIKit/UIWebDocumentView-Forms.h>
91 #import <UIKit/NSString-UIStringDrawing.h>
94 #import <UIKit/UIActionSheet-Private.h>
95 #import <UIKit/UIControl-UIControlPrivate.h>
96 #import <UIKit/UIImage-UIImageDeprecated.h>
97 #import <UIKit/UIImage-UIImagePrivate.h>
98 #import <UIKit/UINavigationBar-Static.h>
99 #import <UIKit/UIProgressHUD-Deprecated.h>
100 #import <UIKit/UIToolbar-UIButtonBarPrivate.h>
101 #import <UIKit/UIView-Deprecated.h>
102 #import <UIKit/UIWindow-Static.h>
105 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
107 #include <WebKit/WebFrame.h>
108 #include <WebKit/WebView.h>
113 #include <ext/stdio_filebuf.h>
115 #include <apt-pkg/acquire.h>
116 #include <apt-pkg/acquire-item.h>
117 #include <apt-pkg/algorithms.h>
118 #include <apt-pkg/cachefile.h>
119 #include <apt-pkg/clean.h>
120 #include <apt-pkg/configuration.h>
121 #include <apt-pkg/debmetaindex.h>
122 #include <apt-pkg/error.h>
123 #include <apt-pkg/init.h>
124 #include <apt-pkg/mmap.h>
125 #include <apt-pkg/pkgrecords.h>
126 #include <apt-pkg/sha1.h>
127 #include <apt-pkg/sourcelist.h>
128 #include <apt-pkg/sptr.h>
130 #include <sys/sysctl.h>
135 #include <mach-o/nlist.h>
145 #define UIWebView UIWebDocumentView
147 #import "BrowserView.h"
148 #import "ResetView.h"
149 #import "UICaboodle.h"
152 @interface WebView (Cydia)
153 - (void) _setLayoutInterval:(float)interval;
156 /* iPhoneOS 2.0 Compatibility {{{ */
158 @interface UICGColor : NSObject {
161 - (id) initWithCGColor:(CGColorRef)color;
167 + (id)systemFontOfSize:(float)fp8;
168 + (id)boldSystemFontOfSize:(float)fp8;
169 - (UIFont *) fontWithSize:(CGFloat)size;
172 @interface NSObject (iPhoneOS)
173 - (CGColorRef) cgColor;
174 - (CGColorRef) CGColor;
178 @implementation NSObject (iPhoneOS)
180 - (CGColorRef) cgColor {
181 return [self CGColor];
184 - (CGColorRef) CGColor {
185 return (CGColorRef) self;
189 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
194 @interface UITextView (iPhoneOS)
195 - (void) setTextSize:(float)size;
198 @implementation UITextView (iPhoneOS)
200 - (void) setTextSize:(float)size {
201 [self setFont:[[self font] fontWithSize:size]];
208 @interface UIApplication (IdleTimer)
209 - (void) setIdleTimerDisabled:(char)arg0;
212 extern "C" int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
214 extern NSString *kUIButtonBarButtonAction;
215 extern NSString *kUIButtonBarButtonInfo;
216 extern NSString *kUIButtonBarButtonInfoOffset;
217 extern NSString *kUIButtonBarButtonSelectedInfo;
218 extern NSString *kUIButtonBarButtonStyle;
219 extern NSString *kUIButtonBarButtonTag;
220 extern NSString *kUIButtonBarButtonTarget;
221 extern NSString *kUIButtonBarButtonTitle;
222 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
223 extern NSString *kUIButtonBarButtonTitleWidth;
224 extern NSString *kUIButtonBarButtonType;
227 kUIProgressIndicatorStyleLargeWhite = 0,
228 kUIProgressIndicatorStyleMediumWhite = 1,
229 kUIProgressIndicatorStyleSmallWhite = 3,
230 kUIProgressIndicatorStyleSmallBlack = 4
231 } UIProgressIndicatorStyle;
234 kUIControlEventMouseDown = 1 << 0,
235 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
236 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
237 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
238 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
239 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
240 } UIControlEventMasks;
242 @interface NSString (UIKit)
243 - (NSString *) stringByAddingPercentEscapes;
244 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
247 @interface NSString (Cydia)
248 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
249 - (NSComparisonResult) compareByPath:(NSString *)other;
252 @implementation NSString (Cydia)
254 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
255 char data[length + 1];
256 memcpy(data, bytes, length);
258 return [NSString stringWithUTF8String:data];
261 - (NSComparisonResult) compareByPath:(NSString *)other {
262 NSString *prefix = [self commonPrefixWithString:other options:0];
263 size_t length = [prefix length];
265 NSRange lrange = NSMakeRange(length, [self length] - length);
266 NSRange rrange = NSMakeRange(length, [other length] - length);
268 lrange = [self rangeOfString:@"/" options:0 range:lrange];
269 rrange = [other rangeOfString:@"/" options:0 range:rrange];
271 NSComparisonResult value;
273 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
274 value = NSOrderedSame;
275 else if (lrange.location == NSNotFound)
276 value = NSOrderedAscending;
277 else if (rrange.location == NSNotFound)
278 value = NSOrderedDescending;
280 value = NSOrderedSame;
282 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
283 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
284 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
285 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
287 NSComparisonResult result = [lpath compare:rpath];
288 return result == NSOrderedSame ? value : result;
293 /* Perl-Compatible RegEx {{{ */
303 Pcre(const char *regex) :
308 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
311 fprintf(stderr, "%d:%s\n", offset, error);
315 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
316 matches_ = new int[(capture_ + 1) * 3];
324 NSString *operator [](size_t match) {
325 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
328 bool operator ()(NSString *data) {
329 // XXX: length is for characters, not for bytes
330 return operator ()([data UTF8String], [data length]);
333 bool operator ()(const char *data, size_t size) {
335 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
339 /* Mime Addresses {{{ */
340 @interface Address : NSObject {
346 - (NSString *) address;
348 + (Address *) addressWithString:(NSString *)string;
349 - (Address *) initWithString:(NSString *)string;
352 @implementation Address
361 - (NSString *) name {
365 - (NSString *) address {
369 + (Address *) addressWithString:(NSString *)string {
370 return [[[Address alloc] initWithString:string] autorelease];
373 + (NSArray *) _attributeKeys {
374 return [NSArray arrayWithObjects:@"address", @"name", nil];
377 - (NSArray *) attributeKeys {
378 return [[self class] _attributeKeys];
381 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
382 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
385 - (Address *) initWithString:(NSString *)string {
386 if ((self = [super init]) != nil) {
387 const char *data = [string UTF8String];
388 size_t size = [string length];
390 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
392 if (address_r(data, size)) {
393 name_ = [address_r[1] retain];
394 address_ = [address_r[2] retain];
396 name_ = [[NSString alloc]
399 encoding:kCFStringEncodingUTF8
409 /* CoreGraphics Primitives {{{ */
420 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
423 Set(space, red, green, blue, alpha);
428 CGColorRelease(color_);
435 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
437 float color[] = {red, green, blue, alpha};
438 color_ = CGColorCreate(space, color);
441 operator CGColorRef() {
457 extern "C" void UISetColor(CGColorRef color);
459 /* Random Global Variables {{{ */
460 static const int PulseInterval_ = 50000;
461 static const int ButtonBarHeight_ = 48;
462 static const float KeyboardTime_ = 0.3f;
463 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
465 static CGColor Blue_;
466 static CGColor Blueish_;
467 static CGColor Black_;
468 static CGColor Clear_;
470 static CGColor White_;
471 static CGColor Gray_;
473 static NSString *App_;
474 static NSString *Home_;
475 static BOOL Sounds_Keyboard_;
477 static BOOL Advanced_;
479 static BOOL Ignored_;
481 static UIFont *Font12_;
482 static UIFont *Font12Bold_;
483 static UIFont *Font14_;
484 static UIFont *Font18Bold_;
485 static UIFont *Font22Bold_;
487 const char *Firmware_ = NULL;
488 const char *Machine_ = NULL;
489 const char *SerialNumber_ = NULL;
496 CGColorSpaceRef space_;
498 #define FW_LEAST(major, minor, bugfix) \
499 (major < Major_ || major == Major_ && \
500 (minor < Minor_ || minor == Minor_ && \
506 static NSDictionary *SectionMap_;
507 static NSMutableDictionary *Metadata_;
508 static _transient NSMutableDictionary *Settings_;
509 static _transient NSString *Role_;
510 static _transient NSMutableDictionary *Packages_;
511 static _transient NSMutableDictionary *Sections_;
512 static _transient NSMutableDictionary *Sources_;
513 static bool Changed_;
516 NSString *GetLastUpdate() {
517 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
520 return @"Never or Unknown";
522 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
523 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
525 CFRelease(formatter);
527 return [(NSString *) formatted autorelease];
530 /* Display Helpers {{{ */
531 inline float Interpolate(float begin, float end, float fraction) {
532 return (end - begin) * fraction + begin;
535 NSString *SizeString(double size) {
537 while (size > 1024) {
542 static const char *powers_[] = {"B", "kB", "MB", "GB"};
544 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
547 NSString *StripVersion(NSString *version) {
548 NSRange colon = [version rangeOfString:@":"];
549 if (colon.location != NSNotFound)
550 version = [version substringFromIndex:(colon.location + 1)];
554 static const float TextViewOffset_ = 22;
556 UITextView *GetTextView(NSString *value, float left, bool html) {
557 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
558 [text setEditable:NO];
559 [text setTextSize:16];
561 [text setHTML:value];
563 [text setText:value];
564 [text setEnabled:NO];
566 [text setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
568 CGRect frame = [text frame];
569 [text setFrame:frame];
570 CGRect rect = [text visibleTextRect];
571 frame.size.height = rect.size.height;
572 [text setFrame:frame];
577 NSString *Simplify(NSString *title) {
578 const char *data = [title UTF8String];
579 size_t size = [title length];
581 static Pcre square_r("^\\[(.*)\\]$");
582 if (square_r(data, size))
583 return Simplify(square_r[1]);
585 static Pcre paren_r("^\\((.*)\\)$");
586 if (paren_r(data, size))
587 return Simplify(paren_r[1]);
589 static Pcre title_r("^(.*?) \\(.*\\)$");
590 if (title_r(data, size))
591 return Simplify(title_r[1]);
597 bool isSectionVisible(NSString *section) {
598 NSDictionary *metadata = [Sections_ objectForKey:section];
599 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
600 return hidden == nil || ![hidden boolValue];
603 /* Delegate Prototypes {{{ */
607 @interface NSObject (ProgressDelegate)
610 @implementation NSObject(ProgressDelegate)
612 - (void) _setProgressError:(NSArray *)args {
613 [self performSelector:@selector(setProgressError:forPackage:)
614 withObject:[args objectAtIndex:0]
615 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
621 @protocol ProgressDelegate
622 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
623 - (void) setProgressTitle:(NSString *)title;
624 - (void) setProgressPercent:(float)percent;
625 - (void) addProgressOutput:(NSString *)output;
628 @protocol ConfigurationDelegate
629 - (void) repairWithSelector:(SEL)selector;
630 - (void) setConfigurationData:(NSString *)data;
633 @protocol CydiaDelegate
634 - (void) installPackage:(Package *)package;
635 - (void) removePackage:(Package *)package;
636 - (void) slideUp:(UIActionSheet *)alert;
637 - (void) distUpgrade;
640 - (void) askForSettings;
641 - (UIProgressHUD *) addProgressHUD;
645 /* Status Delegation {{{ */
647 public pkgAcquireStatus
650 _transient NSObject<ProgressDelegate> *delegate_;
658 void setDelegate(id delegate) {
659 delegate_ = delegate;
662 virtual bool MediaChange(std::string media, std::string drive) {
666 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
669 virtual void Fetch(pkgAcquire::ItemDesc &item) {
670 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
673 virtual void Done(pkgAcquire::ItemDesc &item) {
676 virtual void Fail(pkgAcquire::ItemDesc &item) {
678 item.Owner->Status == pkgAcquire::Item::StatIdle ||
679 item.Owner->Status == pkgAcquire::Item::StatDone
683 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
684 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
689 virtual bool Pulse(pkgAcquire *Owner) {
690 bool value = pkgAcquireStatus::Pulse(Owner);
693 double(CurrentBytes + CurrentItems) /
694 double(TotalBytes + TotalItems)
697 [delegate_ setProgressPercent:percent];
701 virtual void Start() {
704 virtual void Stop() {
708 /* Progress Delegation {{{ */
713 _transient id<ProgressDelegate> delegate_;
716 virtual void Update() {
717 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
718 [delegate_ setProgressPercent:(Percent / 100)];
727 void setDelegate(id delegate) {
728 delegate_ = delegate;
731 virtual void Done() {
732 [delegate_ setProgressPercent:1];
737 /* Database Interface {{{ */
738 @interface Database : NSObject {
740 pkgDepCache::Policy *policy_;
741 pkgRecords *records_;
742 pkgProblemResolver *resolver_;
743 pkgAcquire *fetcher_;
745 SPtr<pkgPackageManager> manager_;
746 pkgSourceList *list_;
748 NSMutableDictionary *sources_;
749 NSMutableArray *packages_;
751 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
760 - (void) _readCydia:(NSNumber *)fd;
761 - (void) _readStatus:(NSNumber *)fd;
762 - (void) _readOutput:(NSNumber *)fd;
766 - (Package *) packageWithName:(NSString *)name;
769 - (pkgCacheFile &) cache;
770 - (pkgDepCache::Policy *) policy;
771 - (pkgRecords *) records;
772 - (pkgProblemResolver *) resolver;
773 - (pkgAcquire &) fetcher;
774 - (NSArray *) packages;
775 - (NSArray *) sources;
784 - (void) updateWithStatus:(Status &)status;
786 - (void) setDelegate:(id)delegate;
787 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
791 /* Source Class {{{ */
792 @interface Source : NSObject {
793 NSString *description_;
798 NSString *distribution_;
802 NSString *defaultIcon_;
804 NSDictionary *record_;
808 - (Source *) initWithMetaIndex:(metaIndex *)index;
810 - (NSComparisonResult) compareByNameAndType:(Source *)source;
812 - (NSDictionary *) record;
816 - (NSString *) distribution;
822 - (NSString *) description;
823 - (NSString *) label;
824 - (NSString *) origin;
825 - (NSString *) version;
827 - (NSString *) defaultIcon;
831 @implementation Source
835 [distribution_ release];
838 if (description_ != nil)
839 [description_ release];
846 if (defaultIcon_ != nil)
847 [defaultIcon_ release];
854 + (NSArray *) _attributeKeys {
855 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
858 - (NSArray *) attributeKeys {
859 return [[self class] _attributeKeys];
862 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
863 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
866 - (Source *) initWithMetaIndex:(metaIndex *)index {
867 if ((self = [super init]) != nil) {
868 trusted_ = index->IsTrusted();
870 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
871 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
872 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
874 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
875 if (dindex != NULL) {
876 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
878 while (std::getline(release, line)) {
879 std::string::size_type colon(line.find(':'));
880 if (colon == std::string::npos)
883 std::string name(line.substr(0, colon));
884 std::string value(line.substr(colon + 1));
885 while (!value.empty() && value[0] == ' ')
886 value = value.substr(1);
888 if (name == "Default-Icon")
889 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
890 else if (name == "Description")
891 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
892 else if (name == "Label")
893 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
894 else if (name == "Origin")
895 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
896 else if (name == "Version")
897 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
901 record_ = [Sources_ objectForKey:[self key]];
903 record_ = [record_ retain];
907 - (NSComparisonResult) compareByNameAndType:(Source *)source {
908 NSDictionary *lhr = [self record];
909 NSDictionary *rhr = [source record];
912 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
914 NSString *lhs = [self name];
915 NSString *rhs = [source name];
917 if ([lhs length] != 0 && [rhs length] != 0) {
918 unichar lhc = [lhs characterAtIndex:0];
919 unichar rhc = [rhs characterAtIndex:0];
921 if (isalpha(lhc) && !isalpha(rhc))
922 return NSOrderedAscending;
923 else if (!isalpha(lhc) && isalpha(rhc))
924 return NSOrderedDescending;
927 return [lhs caseInsensitiveCompare:rhs];
930 - (NSDictionary *) record {
942 - (NSString *) distribution {
943 return distribution_;
946 - (NSString *) type {
951 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
954 - (NSString *) host {
955 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
958 - (NSString *) name {
959 return origin_ == nil ? [self host] : origin_;
962 - (NSString *) description {
966 - (NSString *) label {
967 return label_ == nil ? [self host] : label_;
970 - (NSString *) origin {
974 - (NSString *) version {
978 - (NSString *) defaultIcon {
984 /* Relationship Class {{{ */
985 @interface Relationship : NSObject {
996 @implementation Relationship
1004 - (NSString *) type {
1012 - (NSString *) name {
1019 /* Package Class {{{ */
1020 NSString *Scour(const char *field, const char *begin, const char *end) {
1021 size_t i(0), l(strlen(field));
1024 const char *name = begin + i;
1025 const char *colon = name + l;
1026 const char *value = colon + 1;
1031 memcmp(name, field, l) == 0
1033 while (value != end && value[0] == ' ')
1035 const char *line = std::find(value, end, '\n');
1036 while (line != value && line[-1] == ' ')
1039 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1041 begin = std::find(begin, end, '\n');
1049 @interface Package : NSObject {
1050 pkgCache::PkgIterator iterator_;
1051 _transient Database *database_;
1052 pkgCache::VerIterator version_;
1053 pkgCache::VerFileIterator file_;
1059 NSString *installed_;
1065 NSString *depiction_;
1066 NSString *homepage_;
1072 NSArray *relationships_;
1075 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1076 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1078 - (pkgCache::PkgIterator) iterator;
1080 - (NSString *) section;
1081 - (Address *) maintainer;
1083 - (NSString *) description;
1084 - (NSString *) index;
1088 - (NSString *) latest;
1089 - (NSString *) installed;
1092 - (BOOL) upgradableAndEssential:(BOOL)essential;
1095 - (BOOL) unfiltered;
1099 - (BOOL) halfConfigured;
1100 - (BOOL) halfInstalled;
1102 - (NSString *) mode;
1105 - (NSString *) name;
1106 - (NSString *) tagline;
1107 - (NSString *) icon;
1108 - (NSString *) homepage;
1109 - (NSString *) depiction;
1110 - (Address *) author;
1112 - (NSArray *) relationships;
1114 - (Source *) source;
1115 - (NSString *) role;
1117 - (BOOL) matches:(NSString *)text;
1119 - (bool) hasSupportingRole;
1120 - (BOOL) hasTag:(NSString *)tag;
1122 - (NSComparisonResult) compareByName:(Package *)package;
1123 - (NSComparisonResult) compareBySection:(Package *)package;
1124 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1125 - (NSComparisonResult) compareForChanges:(Package *)package;
1130 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1131 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1132 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1133 - (NSNumber *) isVisibleInSource:(Source *)source;
1137 @implementation Package
1144 if (installed_ != nil)
1145 [installed_ release];
1153 if (depiction_ != nil)
1154 [depiction_ release];
1155 if (homepage_ != nil)
1156 [homepage_ release];
1157 if (sponsor_ != nil)
1166 if (relationships_ != nil)
1167 [relationships_ release];
1172 + (NSArray *) _attributeKeys {
1173 return [NSArray arrayWithObjects:@"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"section", @"size", @"source", @"sponsor", @"tagline", nil];
1176 - (NSArray *) attributeKeys {
1177 return [[self class] _attributeKeys];
1180 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1181 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1184 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1185 if ((self = [super init]) != nil) {
1186 iterator_ = iterator;
1187 database_ = database;
1189 version_ = [database_ policy]->GetCandidateVer(iterator_);
1190 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1192 if (!version_.end())
1193 file_ = version_.FileList();
1195 pkgCache &cache([database_ cache]);
1196 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1199 pkgCache::VerIterator current = iterator_.CurrentVer();
1200 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1202 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1205 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1207 const char *begin, *end;
1208 parser->GetRec(begin, end);
1210 name_ = Scour("Name", begin, end);
1212 name_ = [name_ retain];
1213 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1214 icon_ = Scour("Icon", begin, end);
1216 icon_ = [icon_ retain];
1217 depiction_ = Scour("Depiction", begin, end);
1218 if (depiction_ != nil)
1219 depiction_ = [depiction_ retain];
1220 homepage_ = Scour("Homepage", begin, end);
1221 if (homepage_ == nil)
1222 homepage_ = Scour("Website", begin, end);
1223 if ([homepage_ isEqualToString:depiction_])
1225 if (homepage_ != nil)
1226 homepage_ = [homepage_ retain];
1227 NSString *sponsor = Scour("Sponsor", begin, end);
1229 sponsor_ = [[Address addressWithString:sponsor] retain];
1230 NSString *author = Scour("Author", begin, end);
1232 author_ = [[Address addressWithString:author] retain];
1233 NSString *tags = Scour("Tag", begin, end);
1235 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1239 for (int i(0), e([tags_ count]); i != e; ++i) {
1240 NSString *tag = [tags_ objectAtIndex:i];
1241 if ([tag hasPrefix:@"role::"]) {
1242 role_ = [[tag substringFromIndex:6] retain];
1247 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1248 if (metadata == nil || [metadata count] == 0) {
1249 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1253 [Packages_ setObject:metadata forKey:id_];
1259 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1260 return [[[Package alloc]
1261 initWithIterator:iterator
1266 - (pkgCache::PkgIterator) iterator {
1270 - (NSString *) section {
1271 const char *section = iterator_.Section();
1272 if (section == NULL)
1275 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1278 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1279 if (NSString *rename = [value objectForKey:@"Rename"]) {
1284 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1287 - (Address *) maintainer {
1290 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1291 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1295 return version_.end() ? 0 : version_->InstalledSize;
1298 - (NSString *) description {
1301 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1302 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1304 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1305 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1306 if ([lines count] < 2)
1309 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1310 for (size_t i(1); i != [lines count]; ++i) {
1311 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1312 [trimmed addObject:trim];
1315 return [trimmed componentsJoinedByString:@"\n"];
1318 - (NSString *) index {
1319 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1320 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1324 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1327 - (NSString *) latest {
1331 - (NSString *) installed {
1336 return !version_.end();
1339 - (BOOL) upgradableAndEssential:(BOOL)essential {
1340 pkgCache::VerIterator current = iterator_.CurrentVer();
1343 return essential && [self essential];
1345 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1346 return !candidate.end() && candidate != current;
1350 - (BOOL) essential {
1351 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1355 return [database_ cache][iterator_].InstBroken();
1358 - (BOOL) unfiltered {
1359 NSString *section = [self section];
1360 return section == nil || isSectionVisible(section);
1364 return [self hasSupportingRole] && [self unfiltered];
1368 unsigned char current = iterator_->CurrentState;
1369 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1372 - (BOOL) halfConfigured {
1373 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1376 - (BOOL) halfInstalled {
1377 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1381 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1382 return state.Mode != pkgDepCache::ModeKeep;
1385 - (NSString *) mode {
1386 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1388 switch (state.Mode) {
1389 case pkgDepCache::ModeDelete:
1390 if ((state.iFlags & pkgDepCache::Purge) != 0)
1395 case pkgDepCache::ModeKeep:
1396 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1401 case pkgDepCache::ModeInstall:
1402 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1403 return @"Reinstall";
1404 else switch (state.Status) {
1406 return @"Downgrade";
1412 return @"New Install";
1425 - (NSString *) name {
1426 return name_ == nil ? id_ : name_;
1429 - (NSString *) tagline {
1433 - (NSString *) icon {
1437 - (NSString *) homepage {
1441 - (NSString *) depiction {
1445 - (Address *) sponsor {
1449 - (Address *) author {
1453 - (NSArray *) relationships {
1454 return relationships_;
1457 - (Source *) source {
1459 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1466 - (NSString *) role {
1470 - (BOOL) matches:(NSString *)text {
1476 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1477 if (range.location != NSNotFound)
1480 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1481 if (range.location != NSNotFound)
1484 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1485 if (range.location != NSNotFound)
1491 - (bool) hasSupportingRole {
1494 if ([role_ isEqualToString:@"enduser"])
1496 if ([Role_ isEqualToString:@"User"])
1498 if ([role_ isEqualToString:@"hacker"])
1500 if ([Role_ isEqualToString:@"Hacker"])
1502 if ([role_ isEqualToString:@"developer"])
1504 if ([Role_ isEqualToString:@"Developer"])
1509 - (BOOL) hasTag:(NSString *)tag {
1510 return tags_ == nil ? NO : [tags_ containsObject:tag];
1513 - (NSComparisonResult) compareByName:(Package *)package {
1514 NSString *lhs = [self name];
1515 NSString *rhs = [package name];
1517 if ([lhs length] != 0 && [rhs length] != 0) {
1518 unichar lhc = [lhs characterAtIndex:0];
1519 unichar rhc = [rhs characterAtIndex:0];
1521 if (isalpha(lhc) && !isalpha(rhc))
1522 return NSOrderedAscending;
1523 else if (!isalpha(lhc) && isalpha(rhc))
1524 return NSOrderedDescending;
1527 return [lhs caseInsensitiveCompare:rhs];
1530 - (NSComparisonResult) compareBySection:(Package *)package {
1531 NSString *lhs = [self section];
1532 NSString *rhs = [package section];
1534 if (lhs == NULL && rhs != NULL)
1535 return NSOrderedAscending;
1536 else if (lhs != NULL && rhs == NULL)
1537 return NSOrderedDescending;
1538 else if (lhs != NULL && rhs != NULL) {
1539 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1540 if (result != NSOrderedSame)
1544 return NSOrderedSame;
1547 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1548 NSString *lhs = [self section];
1549 NSString *rhs = [package section];
1551 if (lhs == NULL && rhs != NULL)
1552 return NSOrderedAscending;
1553 else if (lhs != NULL && rhs == NULL)
1554 return NSOrderedDescending;
1555 else if (lhs != NULL && rhs != NULL) {
1556 NSComparisonResult result = [lhs compare:rhs];
1557 if (result != NSOrderedSame)
1561 return [self compareByName:package];
1564 - (NSComparisonResult) compareForChanges:(Package *)package {
1565 BOOL lhs = [self upgradableAndEssential:YES];
1566 BOOL rhs = [package upgradableAndEssential:YES];
1569 return lhs ? NSOrderedAscending : NSOrderedDescending;
1571 switch ([[self seen] compare:[package seen]]) {
1572 case NSOrderedAscending:
1573 return NSOrderedDescending;
1576 case NSOrderedDescending:
1577 return NSOrderedAscending;
1583 return [self compareByName:package];
1587 pkgProblemResolver *resolver = [database_ resolver];
1588 resolver->Clear(iterator_);
1589 resolver->Protect(iterator_);
1590 pkgCacheFile &cache([database_ cache]);
1591 cache->MarkInstall(iterator_, false);
1592 pkgDepCache::StateCache &state((*cache)[iterator_]);
1593 if (!state.Install())
1594 cache->SetReInstall(iterator_, true);
1598 pkgProblemResolver *resolver = [database_ resolver];
1599 resolver->Clear(iterator_);
1600 resolver->Protect(iterator_);
1601 resolver->Remove(iterator_);
1602 [database_ cache]->MarkDelete(iterator_, true);
1605 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1606 return [NSNumber numberWithBool:(
1607 [self unfiltered] && [self matches:search]
1611 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1612 return [NSNumber numberWithBool:(
1613 (![number boolValue] || [self visible]) && [self installed] != nil
1617 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1618 NSString *section = [self section];
1620 return [NSNumber numberWithBool:(
1622 [self installed] == nil && (
1624 section == nil && [name length] == 0 ||
1625 [name isEqualToString:section]
1630 - (NSNumber *) isVisibleInSource:(Source *)source {
1631 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1636 /* Section Class {{{ */
1637 @interface Section : NSObject {
1643 - (NSComparisonResult) compareByName:(Section *)section;
1644 - (Section *) initWithName:(NSString *)name;
1645 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1646 - (NSString *) name;
1649 - (void) addToCount;
1653 @implementation Section
1660 - (NSComparisonResult) compareByName:(Section *)section {
1661 NSString *lhs = [self name];
1662 NSString *rhs = [section name];
1664 if ([lhs length] != 0 && [rhs length] != 0) {
1665 unichar lhc = [lhs characterAtIndex:0];
1666 unichar rhc = [rhs characterAtIndex:0];
1668 if (isalpha(lhc) && !isalpha(rhc))
1669 return NSOrderedAscending;
1670 else if (!isalpha(lhc) && isalpha(rhc))
1671 return NSOrderedDescending;
1674 return [lhs caseInsensitiveCompare:rhs];
1677 - (Section *) initWithName:(NSString *)name {
1678 return [self initWithName:name row:0];
1681 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1682 if ((self = [super init]) != nil) {
1683 name_ = [name retain];
1688 - (NSString *) name {
1700 - (void) addToCount {
1710 /* Database Implementation {{{ */
1711 @implementation Database
1718 - (void) _readCydia:(NSNumber *)fd {
1719 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1721 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1722 std::istream is(&ib);
1725 static Pcre finish_r("^finish:([^:]*)$");
1727 while (std::getline(is, line)) {
1728 const char *data(line.c_str());
1729 size_t size = line.size();
1730 fprintf(stderr, "C:%s\n", data);
1732 if (finish_r(data, size)) {
1733 NSString *finish = finish_r[1];
1734 int index = [Finishes_ indexOfObject:finish];
1735 if (index != INT_MAX && index > Finish_)
1744 - (void) _readStatus:(NSNumber *)fd {
1745 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1747 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1748 std::istream is(&ib);
1751 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1752 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1754 while (std::getline(is, line)) {
1755 const char *data(line.c_str());
1756 size_t size = line.size();
1757 fprintf(stderr, "S:%s\n", data);
1759 if (conffile_r(data, size)) {
1760 [delegate_ setConfigurationData:conffile_r[1]];
1761 } else if (strncmp(data, "status: ", 8) == 0) {
1762 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1763 [delegate_ setProgressTitle:string];
1764 } else if (pmstatus_r(data, size)) {
1765 std::string type([pmstatus_r[1] UTF8String]);
1766 NSString *id = pmstatus_r[2];
1768 float percent([pmstatus_r[3] floatValue]);
1769 [delegate_ setProgressPercent:(percent / 100)];
1771 NSString *string = pmstatus_r[4];
1773 if (type == "pmerror")
1774 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1775 withObject:[NSArray arrayWithObjects:string, id, nil]
1778 else if (type == "pmstatus")
1779 [delegate_ setProgressTitle:string];
1780 else if (type == "pmconffile")
1781 [delegate_ setConfigurationData:string];
1782 else _assert(false);
1783 } else _assert(false);
1790 - (void) _readOutput:(NSNumber *)fd {
1791 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1793 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1794 std::istream is(&ib);
1797 while (std::getline(is, line)) {
1798 fprintf(stderr, "O:%s\n", line.c_str());
1799 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1810 - (Package *) packageWithName:(NSString *)name {
1811 if (static_cast<pkgDepCache *>(cache_) == NULL)
1813 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1814 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1817 - (Database *) init {
1818 if ((self = [super init]) != nil) {
1825 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1826 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1830 _assert(pipe(fds) != -1);
1833 _config->Set("APT::Keep-Fds::", cydiafd_);
1834 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1837 detachNewThreadSelector:@selector(_readCydia:)
1839 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1842 _assert(pipe(fds) != -1);
1846 detachNewThreadSelector:@selector(_readStatus:)
1848 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1851 _assert(pipe(fds) != -1);
1852 _assert(dup2(fds[0], 0) != -1);
1853 _assert(close(fds[0]) != -1);
1855 input_ = fdopen(fds[1], "a");
1857 _assert(pipe(fds) != -1);
1858 _assert(dup2(fds[1], 1) != -1);
1859 _assert(close(fds[1]) != -1);
1862 detachNewThreadSelector:@selector(_readOutput:)
1864 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1869 - (pkgCacheFile &) cache {
1873 - (pkgDepCache::Policy *) policy {
1877 - (pkgRecords *) records {
1881 - (pkgProblemResolver *) resolver {
1885 - (pkgAcquire &) fetcher {
1889 - (NSArray *) packages {
1893 - (NSArray *) sources {
1894 return [sources_ allValues];
1897 - (void) reloadData {
1916 if (!cache_.Open(progress_, true)) {
1918 if (!_error->PopMessage(error))
1921 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1923 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1924 [delegate_ repairWithSelector:@selector(configure)];
1925 else if (error == "The package lists or status file could not be parsed or opened.")
1926 [delegate_ repairWithSelector:@selector(update)];
1927 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1928 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1929 // else if (error == "The list of sources could not be read.")
1930 else _assert(false);
1935 now_ = [[NSDate date] retain];
1937 policy_ = new pkgDepCache::Policy();
1938 records_ = new pkgRecords(cache_);
1939 resolver_ = new pkgProblemResolver(cache_);
1940 fetcher_ = new pkgAcquire(&status_);
1943 list_ = new pkgSourceList();
1944 _assert(list_->ReadMainList());
1946 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1947 _assert(pkgApplyStatus(cache_));
1949 if (cache_->BrokenCount() != 0) {
1950 _assert(pkgFixBroken(cache_));
1951 _assert(cache_->BrokenCount() == 0);
1952 _assert(pkgMinimizeUpgrade(cache_));
1955 [sources_ removeAllObjects];
1956 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1957 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1958 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1960 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1961 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1965 [packages_ removeAllObjects];
1966 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1967 if (Package *package = [Package packageWithIterator:iterator database:self])
1968 [packages_ addObject:package];
1970 [packages_ sortUsingSelector:@selector(compareByName:)];
1973 - (void) configure {
1974 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1975 system([dpkg UTF8String]);
1983 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1984 _assert(!_error->PendingError());
1987 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1990 public pkgArchiveCleaner
1993 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1998 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2000 while (_error->PopMessage(error))
2001 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
2006 pkgRecords records(cache_);
2008 lock_ = new FileFd();
2009 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2010 _assert(!_error->PendingError());
2013 // XXX: explain this with an error message
2014 _assert(list.ReadMainList());
2016 manager_ = (_system->CreatePM(cache_));
2017 _assert(manager_->GetArchives(fetcher_, &list, &records));
2018 _assert(!_error->PendingError());
2022 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2024 _assert(list.ReadMainList());
2025 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2026 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2029 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2034 bool failed = false;
2035 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2036 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2039 std::string uri = (*item)->DescURI();
2040 std::string error = (*item)->ErrorText;
2042 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
2045 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2046 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2057 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2059 if (_error->PendingError()) {
2064 if (result == pkgPackageManager::Failed) {
2069 if (result != pkgPackageManager::Completed) {
2074 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2076 _assert(list.ReadMainList());
2077 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2078 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2081 if (![before isEqualToArray:after])
2086 _assert(pkgDistUpgrade(cache_));
2090 [self updateWithStatus:status_];
2093 - (void) updateWithStatus:(Status &)status {
2095 _assert(list.ReadMainList());
2098 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2099 _assert(!_error->PendingError());
2101 pkgAcquire fetcher(&status);
2102 _assert(list.GetIndexes(&fetcher));
2104 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2105 bool failed = false;
2106 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2107 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2108 (*item)->Finished();
2112 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2113 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2114 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2117 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2122 - (void) setDelegate:(id)delegate {
2123 delegate_ = delegate;
2124 status_.setDelegate(delegate);
2125 progress_.setDelegate(delegate);
2128 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2129 pkgIndexFile *index(NULL);
2130 list_->FindIndex(file, index);
2131 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2137 /* Confirmation View {{{ */
2138 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2139 if ([packages count] == 0)
2142 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2143 [fields setObject:text forKey:key];
2145 CGColor blue(space_, 0, 0, 0.4, 1);
2146 [text setTextColor:[UIColor colorWithCGColor:blue]];
2149 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2150 if (!iterator.end())
2151 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2152 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2154 pkgCache::PkgIterator package(dep.TargetPkg());
2157 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2164 @protocol ConfirmationViewDelegate
2169 @interface ConfirmationView : UIView {
2170 Database *database_;
2172 UITransitionView *transition_;
2174 UINavigationBar *navbar_;
2175 UIPreferencesTable *table_;
2176 NSMutableDictionary *fields_;
2177 UIActionSheet *essential_;
2183 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2187 @implementation ConfirmationView
2190 [navbar_ setDelegate:nil];
2191 [transition_ setDelegate:nil];
2192 [table_ setDataSource:nil];
2194 [transition_ release];
2199 if (essential_ != nil)
2200 [essential_ release];
2205 [transition_ transition:7 toView:nil];
2209 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2210 if (from != nil && to == nil)
2211 [self removeFromSuperview];
2214 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2217 if (essential_ != nil)
2218 [essential_ popupAlertAnimated:YES];
2222 [delegate_ confirm];
2232 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2233 NSString *context = [sheet context];
2235 if ([context isEqualToString:@"remove"])
2243 [delegate_ confirm];
2248 else if ([context isEqualToString:@"unable"])
2254 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2258 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2260 case 0: return @"Statistics";
2261 case 1: return @"Modifications";
2263 default: _assert(false);
2267 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2270 case 1: return [fields_ count];
2272 default: _assert(false);
2276 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2277 if (group != 1 || row == -1)
2280 _assert(size_t(row) < [fields_ count]);
2281 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2285 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2286 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2287 [cell setShowSelection:NO];
2290 case 0: switch (row) {
2292 [cell setTitle:@"Downloading"];
2293 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2297 [cell setTitle:@"Resuming At"];
2298 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2302 double size([database_ cache]->UsrSize());
2305 [cell setTitle:@"Disk Freeing"];
2306 [cell setValue:SizeString(-size)];
2308 [cell setTitle:@"Disk Using"];
2309 [cell setValue:SizeString(size)];
2313 default: _assert(false);
2317 _assert(size_t(row) < [fields_ count]);
2318 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2319 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2322 default: _assert(false);
2328 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2329 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2330 database_ = database;
2331 delegate_ = delegate;
2333 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2334 [self addSubview:transition_];
2336 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2338 CGSize navsize = [UINavigationBar defaultSize];
2339 CGRect navrect = {{0, 0}, navsize};
2340 CGRect bounds = [overlay_ bounds];
2342 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2343 [navbar_ setDelegate:self];
2345 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2346 [navbar_ pushNavigationItem:navitem];
2347 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2349 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2351 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2352 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2353 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2354 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2355 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2359 pkgDepCache::Policy *policy([database_ policy]);
2361 pkgCacheFile &cache([database_ cache]);
2362 NSArray *packages = [database_ packages];
2363 for (size_t i(0), e = [packages count]; i != e; ++i) {
2364 Package *package = [packages objectAtIndex:i];
2365 pkgCache::PkgIterator iterator = [package iterator];
2366 pkgDepCache::StateCache &state(cache[iterator]);
2368 NSString *name([package name]);
2370 if (state.NewInstall())
2371 [installing addObject:name];
2372 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2373 [reinstalling addObject:name];
2374 else if (state.Upgrade())
2375 [upgrading addObject:name];
2376 else if (state.Downgrade())
2377 [downgrading addObject:name];
2378 else if (state.Delete()) {
2379 if ([package essential])
2381 [removing addObject:name];
2384 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2385 substrate_ |= DepSubstrate(iterator.CurrentVer());
2390 else if (Advanced_ || true) {
2391 essential_ = [[UIActionSheet alloc]
2392 initWithTitle:@"Removing Essentials"
2393 buttons:[NSArray arrayWithObjects:
2394 @"Cancel Operation (Safe)",
2395 @"Force Removal (Unsafe)",
2397 defaultButtonIndex:0
2403 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2405 [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."];
2407 essential_ = [[UIActionSheet alloc]
2408 initWithTitle:@"Unable to Comply"
2409 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2410 defaultButtonIndex:0
2415 [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."];
2418 AddTextView(fields_, installing, @"Installing");
2419 AddTextView(fields_, reinstalling, @"Reinstalling");
2420 AddTextView(fields_, upgrading, @"Upgrading");
2421 AddTextView(fields_, downgrading, @"Downgrading");
2422 AddTextView(fields_, removing, @"Removing");
2424 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2425 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2428 [table_ setReusesTableCells:YES];
2429 [table_ setDataSource:self];
2430 [table_ reloadData];
2432 [overlay_ addSubview:navbar_];
2433 [overlay_ addSubview:table_];
2435 [view addSubview:self];
2437 [transition_ setDelegate:self];
2439 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2440 [transition_ transition:0 toView:blank];
2441 [transition_ transition:3 toView:overlay_];
2448 /* Progress Data {{{ */
2449 @interface ProgressData : NSObject {
2455 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2462 @implementation ProgressData
2464 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2465 if ((self = [super init]) != nil) {
2466 selector_ = selector;
2486 /* Progress View {{{ */
2487 @interface ProgressView : UIView <
2488 ConfigurationDelegate,
2491 _transient Database *database_;
2493 UIView *background_;
2494 UITransitionView *transition_;
2496 UINavigationBar *navbar_;
2497 UIProgressBar *progress_;
2498 UITextView *output_;
2499 UITextLabel *status_;
2500 UIPushButton *close_;
2503 SHA1SumValue springlist_;
2506 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2508 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2509 - (void) setContentView:(UIView *)view;
2512 - (void) _retachThread;
2513 - (void) _detachNewThreadData:(ProgressData *)data;
2514 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2520 @protocol ProgressViewDelegate
2521 - (void) progressViewIsComplete:(ProgressView *)sender;
2524 @implementation ProgressView
2527 [transition_ setDelegate:nil];
2528 [navbar_ setDelegate:nil];
2531 if (background_ != nil)
2532 [background_ release];
2533 [transition_ release];
2536 [progress_ release];
2543 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2544 if (bootstrap_ && from == overlay_ && to == view_)
2548 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2549 if ((self = [super initWithFrame:frame]) != nil) {
2550 database_ = database;
2551 delegate_ = delegate;
2553 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2554 [transition_ setDelegate:self];
2556 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2559 [overlay_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2561 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2562 [background_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2563 [self addSubview:background_];
2566 [self addSubview:transition_];
2568 CGSize navsize = [UINavigationBar defaultSize];
2569 CGRect navrect = {{0, 0}, navsize};
2571 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2572 [overlay_ addSubview:navbar_];
2574 [navbar_ setBarStyle:1];
2575 [navbar_ setDelegate:self];
2577 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2578 [navbar_ pushNavigationItem:navitem];
2580 CGRect bounds = [overlay_ bounds];
2581 CGSize prgsize = [UIProgressBar defaultSize];
2584 (bounds.size.width - prgsize.width) / 2,
2585 bounds.size.height - prgsize.height - 20
2588 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2589 [progress_ setStyle:0];
2591 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2593 bounds.size.height - prgsize.height - 50,
2594 bounds.size.width - 20,
2598 [status_ setColor:[UIColor colorWithCGColor:White_]];
2599 [status_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2601 [status_ setCentersHorizontally:YES];
2602 //[status_ setFont:font];
2604 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2606 navrect.size.height + 20,
2607 bounds.size.width - 20,
2608 bounds.size.height - navsize.height - 62 - navrect.size.height
2611 //[output_ setTextFont:@"Courier New"];
2612 [output_ setTextSize:12];
2614 [output_ setTextColor:[UIColor colorWithCGColor:White_]];
2615 [output_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2617 [output_ setMarginTop:0];
2618 [output_ setAllowsRubberBanding:YES];
2619 [output_ setEditable:NO];
2621 [overlay_ addSubview:output_];
2623 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2625 bounds.size.height - prgsize.height - 50,
2626 bounds.size.width - 20,
2630 [close_ setAutosizesToFit:NO];
2631 [close_ setDrawsShadow:YES];
2632 [close_ setStretchBackground:YES];
2633 [close_ setEnabled:YES];
2635 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2636 [close_ setTitleFont:bold];
2638 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2639 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2640 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2644 - (void) setContentView:(UIView *)view {
2645 view_ = [view retain];
2648 - (void) resetView {
2649 [transition_ transition:6 toView:view_];
2652 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2653 NSString *context = [sheet context];
2654 if ([context isEqualToString:@"conffile"]) {
2655 FILE *input = [database_ input];
2659 fprintf(input, "N\n");
2663 fprintf(input, "Y\n");
2674 - (void) closeButtonPushed {
2677 [delegate_ progressViewIsComplete:self];
2682 [delegate_ suspendWithAnimation:YES];
2686 system("launchctl stop com.apple.SpringBoard");
2690 system("launchctl unload /System/Library/LaunchDaemons/com.apple.SpringBoard.plist; launchctl load /System/Library/LaunchDaemons/com.apple.SpringBoard.plist");
2699 - (void) _retachThread {
2700 UINavigationItem *item = [navbar_ topItem];
2701 [item setTitle:@"Complete"];
2703 [overlay_ addSubview:close_];
2704 [progress_ removeFromSuperview];
2705 [status_ removeFromSuperview];
2708 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2709 MMap mmap(file, MMap::ReadOnly);
2711 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2712 if (!(springlist_ == sha1.Result()))
2717 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2718 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2719 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2720 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2721 case 4: [close_ setTitle:@"Reboot Device"]; break;
2725 notify_post("com.apple.mobile.application_installed");
2728 [delegate_ setStatusBarShowsProgress:NO];
2733 - (void) _detachNewThreadData:(ProgressData *)data {
2734 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2736 [[data target] performSelector:[data selector] withObject:[data object]];
2739 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2744 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2745 UINavigationItem *item = [navbar_ topItem];
2746 [item setTitle:title];
2748 [status_ setText:nil];
2749 [output_ setText:@""];
2750 [progress_ setProgress:0];
2752 [close_ removeFromSuperview];
2753 [overlay_ addSubview:progress_];
2754 [overlay_ addSubview:status_];
2756 [delegate_ setStatusBarShowsProgress:YES];
2760 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2761 MMap mmap(file, MMap::ReadOnly);
2763 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2764 springlist_ = sha1.Result();
2767 [transition_ transition:6 toView:overlay_];
2770 detachNewThreadSelector:@selector(_detachNewThreadData:)
2772 withObject:[[ProgressData alloc]
2773 initWithSelector:selector
2780 - (void) repairWithSelector:(SEL)selector {
2782 detachNewThreadSelector:selector
2785 title:@"Repairing..."
2789 - (void) setConfigurationData:(NSString *)data {
2791 performSelectorOnMainThread:@selector(_setConfigurationData:)
2797 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2798 Package *package = id == nil ? nil : [database_ packageWithName:id];
2800 UIActionSheet *sheet = [[[UIActionSheet alloc]
2801 initWithTitle:(package == nil ? @"Source Error" : [package name])
2802 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2803 defaultButtonIndex:0
2808 [sheet setBodyText:error];
2809 [sheet popupAlertAnimated:YES];
2812 - (void) setProgressTitle:(NSString *)title {
2814 performSelectorOnMainThread:@selector(_setProgressTitle:)
2820 - (void) setProgressPercent:(float)percent {
2822 performSelectorOnMainThread:@selector(_setProgressPercent:)
2823 withObject:[NSNumber numberWithFloat:percent]
2828 - (void) addProgressOutput:(NSString *)output {
2830 performSelectorOnMainThread:@selector(_addProgressOutput:)
2836 - (void) _setConfigurationData:(NSString *)data {
2837 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2839 _assert(conffile_r(data));
2841 NSString *ofile = conffile_r[1];
2842 //NSString *nfile = conffile_r[2];
2844 UIActionSheet *sheet = [[[UIActionSheet alloc]
2845 initWithTitle:@"Configuration Upgrade"
2846 buttons:[NSArray arrayWithObjects:
2847 @"Keep My Old Copy",
2848 @"Accept The New Copy",
2849 // XXX: @"See What Changed",
2851 defaultButtonIndex:0
2856 [sheet setBodyText:[NSString stringWithFormat:
2857 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2860 [sheet popupAlertAnimated:YES];
2863 - (void) _setProgressTitle:(NSString *)title {
2864 [status_ setText:[title stringByAppendingString:@"..."]];
2867 - (void) _setProgressPercent:(NSNumber *)percent {
2868 [progress_ setProgress:[percent floatValue]];
2871 - (void) _addProgressOutput:(NSString *)output {
2872 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2873 CGSize size = [output_ contentSize];
2874 CGRect rect = {{0, size.height}, {size.width, 0}};
2875 [output_ scrollRectToVisible:rect animated:YES];
2878 - (BOOL) isRunning {
2885 /* Package Cell {{{ */
2886 @interface PackageCell : UISimpleTableCell {
2889 NSString *description_;
2891 //UIImageView *trusted_;
2893 UIImageView *badge_;
2894 UITextLabel *status_;
2898 - (PackageCell *) init;
2899 - (void) setPackage:(Package *)package;
2901 + (int) heightForPackage:(Package *)package;
2905 @implementation PackageCell
2907 - (void) clearPackage {
2918 if (description_ != nil) {
2919 [description_ release];
2923 if (source_ != nil) {
2930 [self clearPackage];
2935 //[trusted_ release];
2939 - (PackageCell *) init {
2940 if ((self = [super init]) != nil) {
2942 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2944 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2945 [status_ setBackgroundColor:Clear_];
2946 [status_ setFont:small];
2951 - (void) setPackage:(Package *)package {
2952 [self clearPackage];
2954 Source *source = [package source];
2957 if (NSString *icon = [package icon])
2958 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2959 if (icon_ == nil) if (NSString *section = [package section])
2960 icon_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, Simplify(section)]];
2961 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2962 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2964 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2966 icon_ = [icon_ retain];
2968 name_ = [[package name] retain];
2969 description_ = [[package tagline] retain];
2971 NSString *label = nil;
2972 bool trusted = false;
2974 if (source != nil) {
2975 label = [source label];
2976 trusted = [source trusted];
2977 } else if ([[package id] isEqualToString:@"firmware"])
2980 label = @"Unknown/Local";
2982 NSString *from = [NSString stringWithFormat:@"from %@", label];
2984 NSString *section = Simplify([package section]);
2985 if (section != nil && ![section isEqualToString:label])
2986 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2988 source_ = [from retain];
2991 [badge_ removeFromSuperview];
2992 [status_ removeFromSuperview];
2994 if (NSString *mode = [package mode]) {
2995 [badge_ setImage:[UIImage applicationImageNamed:
2996 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2999 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3000 [status_ setColor:Blueish_];
3001 } else if ([package half]) {
3002 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3003 [status_ setText:@"Package Damaged"];
3004 [status_ setColor:Red_];
3006 [badge_ setImage:nil];
3007 [status_ setText:nil];
3011 [self addSubview:badge_];
3012 [self addSubview:status_];
3017 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3019 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3026 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3027 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3031 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3033 [super drawContentInRect:rect selected:selected];
3036 + (int) heightForPackage:(Package *)package {
3037 NSString *tagline([package tagline]);
3038 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3040 if ([package hasMode] || [package half])
3049 /* Section Cell {{{ */
3050 @interface SectionCell : UISimpleTableCell {
3055 _UISwitchSlider *switch_;
3060 - (void) setSection:(Section *)section editing:(BOOL)editing;
3064 @implementation SectionCell
3066 - (void) clearSection {
3067 if (section_ != nil) {
3077 if (count_ != nil) {
3084 [self clearSection];
3091 if ((self = [super init]) != nil) {
3092 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3094 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3095 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3099 - (void) onSwitch:(id)sender {
3100 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3101 if (metadata == nil) {
3102 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3103 [Sections_ setObject:metadata forKey:section_];
3107 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3110 - (void) setSection:(Section *)section editing:(BOOL)editing {
3111 if (editing != editing_) {
3113 [switch_ removeFromSuperview];
3115 [self addSubview:switch_];
3119 [self clearSection];
3121 if (section == nil) {
3122 name_ = [@"All Packages" retain];
3125 section_ = [section name];
3126 if (section_ != nil)
3127 section_ = [section_ retain];
3128 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3129 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3132 [switch_ setValue:isSectionVisible(section_) animated:NO];
3136 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3137 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3144 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3146 CGSize size = [count_ sizeWithFont:Font14_];
3150 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3152 [super drawContentInRect:rect selected:selected];
3158 /* File Table {{{ */
3159 @interface FileTable : RVPage {
3160 _transient Database *database_;
3163 NSMutableArray *files_;
3167 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3168 - (void) setPackage:(Package *)package;
3172 @implementation FileTable
3175 if (package_ != nil)
3184 - (int) numberOfRowsInTable:(UITable *)table {
3185 return files_ == nil ? 0 : [files_ count];
3188 - (float) table:(UITable *)table heightForRow:(int)row {
3192 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3193 if (reusing == nil) {
3194 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3195 UIFont *font = [UIFont systemFontOfSize:16];
3196 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3198 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3202 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3206 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3207 if ((self = [super initWithBook:book]) != nil) {
3208 database_ = database;
3210 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3212 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3213 [self addSubview:list_];
3215 UITableColumn *column = [[[UITableColumn alloc]
3216 initWithTitle:@"Name"
3218 width:[self frame].size.width
3221 [list_ setDataSource:self];
3222 [list_ setSeparatorStyle:1];
3223 [list_ addTableColumn:column];
3224 [list_ setDelegate:self];
3225 [list_ setReusesTableCells:YES];
3229 - (void) setPackage:(Package *)package {
3230 if (package_ != nil) {
3231 [package_ autorelease];
3240 [files_ removeAllObjects];
3242 if (package != nil) {
3243 package_ = [package retain];
3244 name_ = [[package id] retain];
3246 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3249 std::ifstream fin([path UTF8String]);
3251 while (std::getline(fin, line))
3252 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3255 if ([files_ count] != 0) {
3256 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3257 [files_ removeObjectAtIndex:0];
3258 [files_ sortUsingSelector:@selector(compareByPath:)];
3260 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3261 [stack addObject:@"/"];
3263 for (int i(0), e([files_ count]); i != e; ++i) {
3264 NSString *file = [files_ objectAtIndex:i];
3265 while (![file hasPrefix:[stack lastObject]])
3266 [stack removeLastObject];
3267 NSString *directory = [stack lastObject];
3268 [stack addObject:[file stringByAppendingString:@"/"]];
3269 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3270 ([stack count] - 2) * 3, "",
3271 [file substringFromIndex:[directory length]]
3280 - (void) resetViewAnimated:(BOOL)animated {
3281 [list_ resetViewAnimated:animated];
3284 - (void) reloadData {
3285 [self setPackage:[database_ packageWithName:name_]];
3286 [self reloadButtons];
3289 - (NSString *) title {
3290 return @"Installed Files";
3293 - (NSString *) backButtonTitle {
3299 /* Package View {{{ */
3300 @interface PackageView : BrowserView {
3303 NSMutableArray *buttons_;
3306 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3307 - (void) setPackage:(Package *)package;
3311 @implementation PackageView
3314 if (package_ != nil)
3322 - (void) _clickButtonWithName:(NSString *)name {
3323 if ([name isEqualToString:@"Install"])
3324 [delegate_ installPackage:package_];
3325 else if ([name isEqualToString:@"Reinstall"])
3326 [delegate_ installPackage:package_];
3327 else if ([name isEqualToString:@"Remove"])
3328 [delegate_ removePackage:package_];
3329 else if ([name isEqualToString:@"Upgrade"])
3330 [delegate_ installPackage:package_];
3331 else _assert(false);
3334 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3335 int count = [buttons_ count];
3336 _assert(count != 0);
3337 _assert(button <= count + 1);
3339 if (count != button - 1)
3340 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3345 #include "internals.h"
3347 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3348 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3349 return [super webView:sender didFinishLoadForFrame:frame];
3352 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3353 [window setValue:package_ forKey:@"package"];
3356 - (void) _rightButtonClicked {
3357 /*[super _rightButtonClicked];
3360 int count = [buttons_ count];
3361 _assert(count != 0);
3364 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3366 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3367 [buttons addObjectsFromArray:buttons_];
3368 [buttons addObject:@"Cancel"];
3370 [delegate_ slideUp:[[[UIActionSheet alloc]
3373 defaultButtonIndex:2
3380 - (NSString *) _rightButtonTitle {
3381 int count = [buttons_ count];
3382 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3385 - (NSString *) title {
3389 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3390 if ((self = [super initWithBook:book database:database]) != nil) {
3391 database_ = database;
3392 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3396 - (void) setPackage:(Package *)package {
3397 if (package_ != nil) {
3398 [package_ autorelease];
3407 [buttons_ removeAllObjects];
3409 if (package != nil) {
3410 package_ = [package retain];
3411 name_ = [[package id] retain];
3413 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3415 if ([package_ source] == nil);
3416 else if ([package_ upgradableAndEssential:NO])
3417 [buttons_ addObject:@"Upgrade"];
3418 else if ([package_ installed] == nil)
3419 [buttons_ addObject:@"Install"];
3421 [buttons_ addObject:@"Reinstall"];
3422 if ([package_ installed] != nil)
3423 [buttons_ addObject:@"Remove"];
3427 - (void) reloadData {
3428 [self setPackage:[database_ packageWithName:name_]];
3429 [self reloadButtons];
3434 /* Package Table {{{ */
3435 @interface PackageTable : RVPage {
3436 _transient Database *database_;
3440 NSMutableArray *packages_;
3441 NSMutableArray *sections_;
3442 UISectionList *list_;
3445 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3447 - (void) setDelegate:(id)delegate;
3448 - (void) setObject:(id)object;
3450 - (void) reloadData;
3451 - (void) resetCursor;
3453 - (UISectionList *) list;
3455 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3459 @implementation PackageTable
3462 [list_ setDataSource:nil];
3467 [packages_ release];
3468 [sections_ release];
3473 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3474 return [sections_ count];
3477 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3478 return [[sections_ objectAtIndex:section] name];
3481 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3482 return [[sections_ objectAtIndex:section] row];
3485 - (int) numberOfRowsInTable:(UITable *)table {
3486 return [packages_ count];
3489 - (float) table:(UITable *)table heightForRow:(int)row {
3490 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3493 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3495 reusing = [[[PackageCell alloc] init] autorelease];
3496 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3500 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3504 - (void) tableRowSelected:(NSNotification *)notification {
3505 int row = [[notification object] selectedRow];
3509 Package *package = [packages_ objectAtIndex:row];
3510 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3511 [view setDelegate:delegate_];
3512 [view setPackage:package];
3513 [book_ pushPage:view];
3516 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3517 if ((self = [super initWithBook:book]) != nil) {
3518 database_ = database;
3519 title_ = [title retain];
3521 object_ = object == nil ? nil : [object retain];
3523 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3524 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3526 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3527 [list_ setDataSource:self];
3529 UITableColumn *column = [[[UITableColumn alloc]
3530 initWithTitle:@"Name"
3532 width:[self frame].size.width
3535 UITable *table = [list_ table];
3536 [table setSeparatorStyle:1];
3537 [table addTableColumn:column];
3538 [table setDelegate:self];
3539 [table setReusesTableCells:YES];
3541 [self addSubview:list_];
3546 - (void) setDelegate:(id)delegate {
3547 delegate_ = delegate;
3550 - (void) setObject:(id)object {
3556 object_ = [object retain];
3559 - (void) reloadData {
3560 NSArray *packages = [database_ packages];
3562 [packages_ removeAllObjects];
3563 [sections_ removeAllObjects];
3565 for (size_t i(0); i != [packages count]; ++i) {
3566 Package *package([packages objectAtIndex:i]);
3567 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3568 [packages_ addObject:package];
3571 Section *section = nil;
3573 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3574 Package *package = [packages_ objectAtIndex:offset];
3575 NSString *name = [package index];
3577 if (section == nil || ![[section name] isEqualToString:name]) {
3578 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3579 [sections_ addObject:section];
3582 [section addToCount];
3588 - (NSString *) title {
3592 - (void) resetViewAnimated:(BOOL)animated {
3593 [list_ resetViewAnimated:animated];
3596 - (void) resetCursor {
3597 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3600 - (UISectionList *) list {
3604 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3605 [list_ setShouldHideHeaderInShortLists:hide];
3611 /* Add Source View {{{ */
3612 @interface AddSourceView : RVPage {
3613 _transient Database *database_;
3616 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3620 @implementation AddSourceView
3622 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3623 if ((self = [super initWithBook:book]) != nil) {
3624 database_ = database;
3630 /* Source Cell {{{ */
3631 @interface SourceCell : UITableCell {
3634 NSString *description_;
3640 - (SourceCell *) initWithSource:(Source *)source;
3644 @implementation SourceCell
3649 [description_ release];
3654 - (SourceCell *) initWithSource:(Source *)source {
3655 if ((self = [super init]) != nil) {
3657 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3659 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3660 icon_ = [icon_ retain];
3662 origin_ = [[source name] retain];
3663 label_ = [[source uri] retain];
3664 description_ = [[source description] retain];
3668 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3670 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3677 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3681 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3685 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3687 [super drawContentInRect:rect selected:selected];
3692 /* Source Table {{{ */
3693 @interface SourceTable : RVPage {
3694 _transient Database *database_;
3695 UISectionList *list_;
3696 NSMutableArray *sources_;
3697 UIActionSheet *alert_;
3701 UIProgressHUD *hud_;
3704 //NSURLConnection *installer_;
3705 NSURLConnection *trivial_bz2_;
3706 NSURLConnection *trivial_gz_;
3707 //NSURLConnection *automatic_;
3712 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3716 @implementation SourceTable
3718 - (void) _deallocConnection:(NSURLConnection *)connection {
3719 if (connection != nil) {
3720 [connection cancel];
3721 //[connection setDelegate:nil];
3722 [connection release];
3727 [[list_ table] setDelegate:nil];
3728 [list_ setDataSource:nil];
3737 //[self _deallocConnection:installer_];
3738 [self _deallocConnection:trivial_gz_];
3739 [self _deallocConnection:trivial_bz2_];
3740 //[self _deallocConnection:automatic_];
3747 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3748 return offset_ == 0 ? 1 : 2;
3751 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3752 switch (section + (offset_ == 0 ? 1 : 0)) {
3753 case 0: return @"Entered by User";
3754 case 1: return @"Installed by Packages";
3762 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3763 switch (section + (offset_ == 0 ? 1 : 0)) {
3765 case 1: return offset_;
3773 - (int) numberOfRowsInTable:(UITable *)table {
3774 return [sources_ count];
3777 - (float) table:(UITable *)table heightForRow:(int)row {
3778 Source *source = [sources_ objectAtIndex:row];
3779 return [source description] == nil ? 56 : 73;
3782 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3783 Source *source = [sources_ objectAtIndex:row];
3784 // XXX: weird warning, stupid selectors ;P
3785 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3788 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3792 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3796 - (void) tableRowSelected:(NSNotification*)notification {
3797 UITable *table([list_ table]);
3798 int row([table selectedRow]);
3802 Source *source = [sources_ objectAtIndex:row];
3804 PackageTable *packages = [[[PackageTable alloc]
3807 title:[source label]
3808 filter:@selector(isVisibleInSource:)
3812 [packages setDelegate:delegate_];
3814 [book_ pushPage:packages];
3817 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3818 Source *source = [sources_ objectAtIndex:row];
3819 return [source record] != nil;
3822 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3823 [[list_ table] setDeleteConfirmationRow:row];
3826 - (void) table:(UITable *)table deleteRow:(int)row {
3827 Source *source = [sources_ objectAtIndex:row];
3828 [Sources_ removeObjectForKey:[source key]];
3829 [delegate_ syncData];
3832 - (void) _endConnection:(NSURLConnection *)connection {
3833 NSURLConnection **field = NULL;
3834 if (connection == trivial_bz2_)
3835 field = &trivial_bz2_;
3836 else if (connection == trivial_gz_)
3837 field = &trivial_gz_;
3838 _assert(field != NULL);
3839 [connection release];
3843 trivial_bz2_ == nil &&
3846 [delegate_ setStatusBarShowsProgress:NO];
3849 [hud_ removeFromSuperview];
3854 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3857 @"./", @"Distribution",
3858 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3860 [delegate_ syncData];
3861 } else if (error_ != nil) {
3862 UIActionSheet *sheet = [[[UIActionSheet alloc]
3863 initWithTitle:@"Verification Error"
3864 buttons:[NSArray arrayWithObjects:@"OK", nil]
3865 defaultButtonIndex:0
3870 [sheet setBodyText:[error_ localizedDescription]];
3871 [sheet popupAlertAnimated:YES];
3873 UIActionSheet *sheet = [[[UIActionSheet alloc]
3874 initWithTitle:@"Did not Find Repository"
3875 buttons:[NSArray arrayWithObjects:@"OK", nil]
3876 defaultButtonIndex:0
3881 [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."];
3882 [sheet popupAlertAnimated:YES];
3888 if (error_ != nil) {
3895 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3896 switch ([response statusCode]) {
3902 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3903 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3905 error_ = [error retain];
3906 [self _endConnection:connection];
3909 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3910 [self _endConnection:connection];
3913 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
3914 NSMutableURLRequest *request = [NSMutableURLRequest
3915 requestWithURL:[NSURL URLWithString:href]
3916 cachePolicy:NSURLRequestUseProtocolCachePolicy
3917 timeoutInterval:20.0
3920 [request setHTTPMethod:method];
3922 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
3925 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3926 NSString *context = [sheet context];
3927 if ([context isEqualToString:@"source"])
3930 NSString *href = [[sheet textField] text];
3932 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
3934 if (![href hasSuffix:@"/"])
3935 href_ = [href stringByAppendingString:@"/"];
3938 href_ = [href_ retain];
3940 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
3941 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
3942 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
3946 hud_ = [delegate_ addProgressHUD];
3947 [hud_ setText:@"Verifying URL"];
3960 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3961 if ((self = [super initWithBook:book]) != nil) {
3962 database_ = database;
3963 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
3965 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
3966 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3967 [list_ setShouldHideHeaderInShortLists:NO];
3969 [self addSubview:list_];
3970 [list_ setDataSource:self];
3972 UITableColumn *column = [[UITableColumn alloc]
3973 initWithTitle:@"Name"
3975 width:[self frame].size.width
3978 UITable *table = [list_ table];
3979 [table setSeparatorStyle:1];
3980 [table addTableColumn:column];
3981 [table setDelegate:self];
3987 - (void) reloadData {
3989 _assert(list.ReadMainList());
3991 [sources_ removeAllObjects];
3992 [sources_ addObjectsFromArray:[database_ sources]];
3993 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
3995 int count = [sources_ count];
3996 for (offset_ = 0; offset_ != count; ++offset_) {
3997 Source *source = [sources_ objectAtIndex:offset_];
3998 if ([source record] == nil)
4005 - (void) resetViewAnimated:(BOOL)animated {
4006 [list_ resetViewAnimated:animated];
4009 - (void) _leftButtonClicked {
4010 /*[book_ pushPage:[[[AddSourceView alloc]
4015 UIActionSheet *sheet = [[[UIActionSheet alloc]
4016 initWithTitle:@"Enter Cydia/APT URL"
4017 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4018 defaultButtonIndex:0
4023 [sheet addTextFieldWithValue:@"http://" label:@""];
4025 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4026 [traits setAutocapitalizationType:0];
4027 [traits setKeyboardType:3];
4028 [traits setAutocorrectionType:1];
4030 [sheet popupAlertAnimated:YES];
4033 - (void) _rightButtonClicked {
4034 UITable *table = [list_ table];
4035 BOOL editing = [table isRowDeletionEnabled];
4036 [table enableRowDeletion:!editing animated:YES];
4037 [book_ reloadButtonsForPage:self];
4040 - (NSString *) title {
4044 - (NSString *) backButtonTitle {
4048 - (NSString *) leftButtonTitle {
4049 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4052 - (NSString *) rightButtonTitle {
4053 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4056 - (RVUINavBarButtonStyle) rightButtonStyle {
4057 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4063 /* Installed View {{{ */
4064 @interface InstalledView : RVPage {
4065 _transient Database *database_;
4066 PackageTable *packages_;
4070 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4074 @implementation InstalledView
4077 [packages_ release];
4081 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4082 if ((self = [super initWithBook:book]) != nil) {
4083 database_ = database;
4085 packages_ = [[PackageTable alloc]
4089 filter:@selector(isInstalledAndVisible:)
4090 with:[NSNumber numberWithBool:YES]
4093 [self addSubview:packages_];
4097 - (void) resetViewAnimated:(BOOL)animated {
4098 [packages_ resetViewAnimated:animated];
4101 - (void) reloadData {
4102 [packages_ reloadData];
4105 - (void) _rightButtonClicked {
4106 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4107 [packages_ reloadData];
4109 [book_ reloadButtonsForPage:self];
4112 - (NSString *) title {
4113 return @"Installed";
4116 - (NSString *) backButtonTitle {
4120 - (NSString *) rightButtonTitle {
4121 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4124 - (RVUINavBarButtonStyle) rightButtonStyle {
4125 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4128 - (void) setDelegate:(id)delegate {
4129 [super setDelegate:delegate];
4130 [packages_ setDelegate:delegate];
4137 @interface HomeView : BrowserView {
4142 @implementation HomeView
4144 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4148 - (void) _leftButtonClicked {
4149 UIActionSheet *sheet = [[[UIActionSheet alloc]
4150 initWithTitle:@"About Cydia Installer"
4151 buttons:[NSArray arrayWithObjects:@"Close", nil]
4152 defaultButtonIndex:0
4158 @"Copyright (C) 2008\n"
4159 "Jay Freeman (saurik)\n"
4160 "saurik@saurik.com\n"
4161 "http://www.saurik.com/\n"
4164 "http://www.theokorigroup.com/\n"
4166 "College of Creative Studies,\n"
4167 "University of California,\n"
4169 "http://www.ccs.ucsb.edu/"
4172 [sheet popupAlertAnimated:YES];
4175 - (NSString *) leftButtonTitle {
4181 /* Manage View {{{ */
4182 @interface ManageView : BrowserView {
4187 @implementation ManageView
4189 - (NSString *) title {
4193 - (void) _leftButtonClicked {
4194 [delegate_ askForSettings];
4197 - (NSString *) leftButtonTitle {
4201 - (NSString *) _rightButtonTitle {
4208 /* Browser Implementation {{{ */
4209 @implementation BrowserView
4212 WebView *webview = [webview_ webView];
4213 [webview setFrameLoadDelegate:nil];
4214 [webview setResourceLoadDelegate:nil];
4215 [webview setUIDelegate:nil];
4217 [scroller_ setDelegate:nil];
4218 [webview_ setDelegate:nil];
4220 [scroller_ release];
4223 [indicator_ release];
4229 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4230 [self loadRequest:[NSURLRequest
4233 timeoutInterval:30.0
4237 - (void) loadURL:(NSURL *)url {
4238 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4241 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4242 NSMutableURLRequest *copy = [request mutableCopy];
4244 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4245 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4246 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4249 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4254 - (void) loadRequest:(NSURLRequest *)request {
4256 [webview_ loadRequest:request];
4259 - (void) reloadURL {
4260 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4261 [urls_ removeLastObject];
4262 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4265 - (WebView *) webView {
4266 return [webview_ webView];
4269 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4270 [scroller_ setContentSize:frame.size];
4273 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4274 [self view:sender didSetFrame:frame];
4277 - (void) pushPage:(RVPage *)page {
4278 [self setBackButtonTitle:title_];
4279 [page setDelegate:delegate_];
4280 [book_ pushPage:page];
4283 - (RVPage *) _pageForPackage:(NSString *)name {
4284 if (Package *package = [database_ packageWithName:name]) {
4285 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4286 [view setPackage:package];
4289 UIActionSheet *sheet = [[[UIActionSheet alloc]
4290 initWithTitle:@"Cannot Locate Package"
4291 buttons:[NSArray arrayWithObjects:@"Close", nil]
4292 defaultButtonIndex:0
4297 [sheet setBodyText:[NSString stringWithFormat:
4298 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4301 [sheet popupAlertAnimated:YES];
4306 - (BOOL) getSpecial:(NSString *)href {
4310 [href hasPrefix:@"http://phobos.apple.com/"] ||
4311 [href hasPrefix:@"mailto:"]
4313 [delegate_ openURL:[NSURL URLWithString:href]];
4314 else if ([href isEqualToString:@"cydia://add-source"])
4315 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4316 else if ([href isEqualToString:@"cydia://sources"])
4317 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4318 else if ([href isEqualToString:@"cydia://packages"])
4319 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4320 else if ([href hasPrefix:@"cydia://files/"]) {
4321 NSString *name = [href substringFromIndex:14];
4323 if (Package *package = [database_ packageWithName:name]) {
4324 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
4325 [files setPackage:package];
4328 } else if ([href hasPrefix:@"apptapp://package/"])
4329 page = [self _pageForPackage:[href substringFromIndex:18]];
4330 else if ([href hasPrefix:@"cydia://package/"])
4331 page = [self _pageForPackage:[href substringFromIndex:16]];
4332 else if (![href hasPrefix:@"apptapp:"] && ![href hasPrefix:@"cydia:"])
4336 [self pushPage:page];
4340 - (void) webView:(WebView *)sender willClickElement:(id)element {
4341 if ([[element localName] isEqualToString:@"img"])
4342 do if ((element = [element parentNode]) == nil)
4344 while (![[element localName] isEqualToString:@"a"]);
4345 if (![element respondsToSelector:@selector(href)])
4347 NSString *href = [element href];
4350 [self getSpecial:href];
4353 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4354 NSURL *url = [request URL];
4355 NSLog(@"Cydia:%@", url);
4356 if ([self getSpecial:[url absoluteString]])
4361 [book_ pushPage:self];
4364 return [self _addHeadersToRequest:request];
4367 - (BOOL) isSpecialScheme:(NSString *)scheme {
4369 [scheme isEqualToString:@"apptapp"] ||
4370 [scheme isEqualToString:@"cydia"] ||
4371 [scheme isEqualToString:@"mailto"];
4374 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4375 if (request != nil) {
4376 NSURL *url = [request URL];
4377 NSString *scheme = [url scheme];
4378 if ([self isSpecialScheme:scheme] || [[url absoluteString] hasPrefix:@"http://phobos.apple.com/"])
4382 [self setBackButtonTitle:title_];
4384 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4385 [browser setDelegate:delegate_];
4387 if (request != nil) {
4388 [browser loadRequest:[self _addHeadersToRequest:request]];
4389 [book_ pushPage:browser];
4392 return [browser webView];
4395 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4396 if ([frame parentFrame] != nil)
4399 title_ = [title retain];
4400 [self setTitle:title];
4403 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4404 if ([frame parentFrame] != nil)
4409 [indicator_ startAnimation];
4410 [self reloadButtons];
4412 if (title_ != nil) {
4417 [self setTitle:@"Loading..."];
4419 WebView *webview = [webview_ webView];
4420 NSString *href = [webview mainFrameURL];
4421 [urls_ addObject:[NSURL URLWithString:href]];
4423 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4425 CGRect webrect = [scroller_ bounds];
4426 webrect.size.height = 0;
4427 [webview_ setFrame:webrect];
4430 - (void) _finishLoading {
4433 [indicator_ stopAnimation];
4434 [self reloadButtons];
4438 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4440 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4443 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4444 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4447 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4448 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4451 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4452 return [webview_ webView:sender didCommitLoadForFrame:frame];
4455 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4456 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4459 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4460 if ([frame parentFrame] == nil)
4461 [self _finishLoading];
4462 return [webview_ webView:sender didFinishLoadForFrame:frame];
4465 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4466 if ([frame parentFrame] != nil)
4468 [self _finishLoading];
4470 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4471 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4472 [[error localizedDescription] stringByAddingPercentEscapes]
4476 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4477 if ((self = [super initWithBook:book]) != nil) {
4478 database_ = database;
4481 struct CGRect bounds = [self bounds];
4483 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4484 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4485 [self addSubview:pinstripe];
4487 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4488 [self addSubview:scroller_];
4490 [scroller_ setScrollingEnabled:YES];
4491 [scroller_ setAdjustForContentSizeChange:YES];
4492 [scroller_ setClipsSubviews:YES];
4493 [scroller_ setAllowsRubberBanding:YES];
4494 [scroller_ setScrollDecelerationFactor:0.99];
4495 [scroller_ setDelegate:self];
4497 CGRect webrect = [scroller_ bounds];
4498 webrect.size.height = 0;
4500 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4501 [scroller_ addSubview:webview_];
4503 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4505 [webview_ setTilingEnabled:YES];
4506 [webview_ setTileMinificationFilter:kCAFilterNearest];
4507 [webview_ setAutoresizes:YES];
4509 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4510 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4511 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4513 [webview_ _setDocumentType:0x4];
4515 [webview_ setZoomsFocusedFormControl:YES];
4516 [webview_ setContentsPosition:7];
4517 [webview_ setEnabledGestures:0xa];
4518 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4519 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4520 [webview_ setDelegate:self];
4521 [webview_ setGestureDelegate:self];
4522 [webview_ setSmoothsFonts:YES];
4524 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4525 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4526 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4528 Package *package([database_ packageWithName:@"cydia"]);
4529 NSString *application = package == nil ? @"Cydia" : [NSString
4530 stringWithFormat:@"Cydia/%@",
4534 WebView *webview = [webview_ webView];
4535 [webview setApplicationNameForUserAgent:application];
4536 [webview setFrameLoadDelegate:self];
4537 [webview setResourceLoadDelegate:self];
4538 [webview setUIDelegate:self];
4540 //[webview _setLayoutInterval:0.5];
4542 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4546 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4547 [webview_ redrawScaledDocument];
4550 - (void) _rightButtonClicked {
4555 - (NSString *) _rightButtonTitle {
4559 - (NSString *) rightButtonTitle {
4560 return loading_ ? @"" : [self _rightButtonTitle];
4563 - (NSString *) title {
4567 - (NSString *) backButtonTitle {
4571 - (void) setPageActive:(BOOL)active {
4573 [book_ addSubview:indicator_];
4575 [indicator_ removeFromSuperview];
4578 - (void) resetViewAnimated:(BOOL)animated {
4581 - (void) setPushed:(bool)pushed {
4588 @interface CYBook : RVBook <
4591 _transient Database *database_;
4593 UIProgressIndicator *indicator_;
4594 UITextLabel *prompt_;
4595 UIProgressBar *progress_;
4599 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4605 /* Install View {{{ */
4606 @interface InstallView : RVPage {
4607 _transient Database *database_;
4608 NSMutableArray *sections_;
4609 NSMutableArray *filtered_;
4610 UITransitionView *transition_;
4616 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4617 - (void) reloadData;
4622 @implementation InstallView
4625 [list_ setDataSource:nil];
4626 [list_ setDelegate:nil];
4628 [sections_ release];
4629 [filtered_ release];
4630 [transition_ release];
4632 [accessory_ release];
4636 - (int) numberOfRowsInTable:(UITable *)table {
4637 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4640 - (float) table:(UITable *)table heightForRow:(int)row {
4644 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4646 reusing = [[[SectionCell alloc] init] autorelease];
4647 [(SectionCell *)reusing setSection:(editing_ ?
4648 [sections_ objectAtIndex:row] :
4649 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4650 ) editing:editing_];
4654 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4658 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4662 - (void) tableRowSelected:(NSNotification *)notification {
4663 int row = [[notification object] selectedRow];
4674 title = @"All Packages";
4676 section = [filtered_ objectAtIndex:(row - 1)];
4677 name = [section name];
4683 title = @"(No Section)";
4687 PackageTable *table = [[[PackageTable alloc]
4691 filter:@selector(isVisiblyUninstalledInSection:)
4695 [table setDelegate:delegate_];
4697 [book_ pushPage:table];
4700 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4701 if ((self = [super initWithBook:book]) != nil) {
4702 database_ = database;
4704 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4705 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4707 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4708 [self addSubview:transition_];
4710 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4711 [transition_ transition:0 toView:list_];
4713 UITableColumn *column = [[[UITableColumn alloc]
4714 initWithTitle:@"Name"
4716 width:[self frame].size.width
4719 [list_ setDataSource:self];
4720 [list_ setSeparatorStyle:1];
4721 [list_ addTableColumn:column];
4722 [list_ setDelegate:self];
4723 [list_ setReusesTableCells:YES];
4729 - (void) reloadData {
4730 NSArray *packages = [database_ packages];
4732 [sections_ removeAllObjects];
4733 [filtered_ removeAllObjects];
4735 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4736 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4738 for (size_t i(0); i != [packages count]; ++i) {
4739 Package *package([packages objectAtIndex:i]);
4740 NSString *name([package section]);
4743 Section *section([sections objectForKey:name]);
4744 if (section == nil) {
4745 section = [[[Section alloc] initWithName:name] autorelease];
4746 [sections setObject:section forKey:name];
4750 if ([package valid] && [package installed] == nil && [package visible])
4751 [filtered addObject:package];
4754 [sections_ addObjectsFromArray:[sections allValues]];
4755 [sections_ sortUsingSelector:@selector(compareByName:)];
4757 [filtered sortUsingSelector:@selector(compareBySection:)];
4759 Section *section = nil;
4760 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4761 Package *package = [filtered objectAtIndex:offset];
4762 NSString *name = [package section];
4764 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4765 section = name == nil ?
4766 [[[Section alloc] initWithName:nil] autorelease] :
4767 [sections objectForKey:name];
4768 [filtered_ addObject:section];
4771 [section addToCount];
4777 - (void) resetView {
4779 [self _rightButtonClicked];
4782 - (void) resetViewAnimated:(BOOL)animated {
4783 [list_ resetViewAnimated:animated];
4786 - (void) _rightButtonClicked {
4787 if ((editing_ = !editing_))
4790 [delegate_ updateData];
4793 [book_ setTitle:[self title] forPage:self];
4794 [book_ reloadButtonsForPage:self];
4797 - (NSString *) title {
4798 return editing_ ? @"Section Visibility" : @"Install by Section";
4801 - (NSString *) backButtonTitle {
4805 - (NSString *) rightButtonTitle {
4806 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4809 - (UIView *) accessoryView {
4815 /* Changes View {{{ */
4816 @interface ChangesView : RVPage {
4817 _transient Database *database_;
4818 NSMutableArray *packages_;
4819 NSMutableArray *sections_;
4820 UISectionList *list_;
4824 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4825 - (void) reloadData;
4829 @implementation ChangesView
4832 [[list_ table] setDelegate:nil];
4833 [list_ setDataSource:nil];
4835 [packages_ release];
4836 [sections_ release];
4841 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4842 return [sections_ count];
4845 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4846 return [[sections_ objectAtIndex:section] name];
4849 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4850 return [[sections_ objectAtIndex:section] row];
4853 - (int) numberOfRowsInTable:(UITable *)table {
4854 return [packages_ count];
4857 - (float) table:(UITable *)table heightForRow:(int)row {
4858 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4861 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4863 reusing = [[[PackageCell alloc] init] autorelease];
4864 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4868 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4872 - (void) tableRowSelected:(NSNotification *)notification {
4873 int row = [[notification object] selectedRow];
4876 Package *package = [packages_ objectAtIndex:row];
4877 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4878 [view setDelegate:delegate_];
4879 [view setPackage:package];
4880 [book_ pushPage:view];
4883 - (void) _leftButtonClicked {
4884 [(CYBook *)book_ update];
4885 [self reloadButtons];
4888 - (void) _rightButtonClicked {
4889 [delegate_ distUpgrade];
4892 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4893 if ((self = [super initWithBook:book]) != nil) {
4894 database_ = database;
4896 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4897 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4899 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4900 [self addSubview:list_];
4902 [list_ setShouldHideHeaderInShortLists:NO];
4903 [list_ setDataSource:self];
4904 //[list_ setSectionListStyle:1];
4906 UITableColumn *column = [[[UITableColumn alloc]
4907 initWithTitle:@"Name"
4909 width:[self frame].size.width
4912 UITable *table = [list_ table];
4913 [table setSeparatorStyle:1];
4914 [table addTableColumn:column];
4915 [table setDelegate:self];
4916 [table setReusesTableCells:YES];
4922 - (void) reloadData {
4923 NSArray *packages = [database_ packages];
4925 [packages_ removeAllObjects];
4926 [sections_ removeAllObjects];
4928 for (size_t i(0); i != [packages count]; ++i) {
4929 Package *package([packages objectAtIndex:i]);
4932 [package installed] == nil && [package valid] && [package visible] ||
4933 [package upgradableAndEssential:NO]
4935 [packages_ addObject:package];
4938 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4940 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4941 Section *section = nil;
4944 bool unseens = false;
4946 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4948 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4949 Package *package = [packages_ objectAtIndex:offset];
4951 if ([package upgradableAndEssential:YES]) {
4953 [upgradable addToCount];
4956 NSDate *seen = [package seen];
4961 name = [@"n/a ?" retain];
4963 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4966 if (section == nil || ![[section name] isEqualToString:name]) {
4967 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4968 [sections_ addObject:section];
4972 [section addToCount];
4976 CFRelease(formatter);
4979 Section *last = [sections_ lastObject];
4980 size_t count = [last count];
4981 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4982 [sections_ removeLastObject];
4986 [sections_ insertObject:upgradable atIndex:0];
4989 [self reloadButtons];
4992 - (void) resetViewAnimated:(BOOL)animated {
4993 [list_ resetViewAnimated:animated];
4996 - (NSString *) leftButtonTitle {
4997 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5000 - (NSString *) rightButtonTitle {
5001 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5004 - (NSString *) title {
5010 /* Search View {{{ */
5011 @protocol SearchViewDelegate
5012 - (void) showKeyboard:(BOOL)show;
5015 @interface SearchView : RVPage {
5017 UISearchField *field_;
5018 UITransitionView *transition_;
5019 PackageTable *table_;
5020 UIPreferencesTable *advanced_;
5026 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5027 - (void) reloadData;
5031 @implementation SearchView
5035 [[field_ textTraits] setEditingDelegate:nil];
5037 [field_ setDelegate:nil];
5039 [accessory_ release];
5041 [transition_ release];
5043 [advanced_ release];
5048 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5052 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5054 case 0: return @"Advanced Search (Coming Soon!)";
5056 default: _assert(false);
5060 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5064 default: _assert(false);
5068 - (void) _showKeyboard:(BOOL)show {
5069 CGSize keysize = [UIKeyboard defaultSize];
5070 CGRect keydown = [book_ pageBounds];
5071 CGRect keyup = keydown;
5072 keyup.size.height -= keysize.height - ButtonBarHeight_;
5074 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5076 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5077 [animation setSignificantRectFields:8];
5080 [animation setStartFrame:keydown];
5081 [animation setEndFrame:keyup];
5083 [animation setStartFrame:keyup];
5084 [animation setEndFrame:keydown];
5087 UIAnimator *animator = [UIAnimator sharedAnimator];
5090 addAnimations:[NSArray arrayWithObjects:animation, nil]
5091 withDuration:(KeyboardTime_ - delay)
5096 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5099 [delegate_ showKeyboard:show];
5103 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5104 [self _showKeyboard:YES];
5107 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5108 [self _showKeyboard:NO];
5111 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5113 NSString *text([field_ text]);
5114 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5120 - (void) textFieldClearButtonPressed:(UITextField *)field {
5124 - (void) keyboardInputShouldDelete:(id)input {
5128 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5129 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5133 [field_ resignFirstResponder];
5138 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5139 if ((self = [super initWithBook:book]) != nil) {
5140 CGRect pageBounds = [book_ pageBounds];
5142 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5143 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5144 [self addSubview:pinstripe];*/
5146 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5147 [self addSubview:transition_];
5149 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5151 [advanced_ setReusesTableCells:YES];
5152 [advanced_ setDataSource:self];
5153 [advanced_ reloadData];
5155 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5156 CGColor dimmed(space_, 0, 0, 0, 0.5);
5157 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5159 table_ = [[PackageTable alloc]
5163 filter:@selector(isUnfilteredAndSearchedForBy:)
5167 [table_ setShouldHideHeaderInShortLists:NO];
5168 [transition_ transition:0 toView:table_];
5177 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5184 [self bounds].size.width - area.origin.x - 18;
5186 area.size.height = [UISearchField defaultHeight];
5188 field_ = [[UISearchField alloc] initWithFrame:area];
5190 UIFont *font = [UIFont systemFontOfSize:16];
5191 [field_ setFont:font];
5193 [field_ setPlaceholder:@"Package Names & Descriptions"];
5194 [field_ setDelegate:self];
5196 [field_ setPaddingTop:3];
5198 UITextInputTraits *traits = [field_ textInputTraits];
5199 [traits setAutocapitalizationType:0];
5200 [traits setAutocorrectionType:1];
5201 [traits setReturnKeyType:6];
5203 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5205 accessory_ = [[UIView alloc] initWithFrame:accrect];
5206 [accessory_ addSubview:field_];
5208 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5209 [configure setShowPressFeedback:YES];
5210 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5211 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5212 [accessory_ addSubview:configure];*/
5218 LKAnimation *animation = [LKTransition animation];
5219 [animation setType:@"oglFlip"];
5220 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5221 [animation setFillMode:@"extended"];
5222 [animation setTransitionFlags:3];
5223 [animation setDuration:10];
5224 [animation setSpeed:0.35];
5225 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5226 [[transition_ _layer] addAnimation:animation forKey:0];
5227 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5228 flipped_ = !flipped_;
5232 - (void) configurePushed {
5233 [field_ resignFirstResponder];
5237 - (void) resetViewAnimated:(BOOL)animated {
5240 [table_ resetViewAnimated:animated];
5243 - (void) reloadData {
5246 [table_ setObject:[field_ text]];
5247 [table_ reloadData];
5248 [table_ resetCursor];
5251 - (UIView *) accessoryView {
5255 - (NSString *) title {
5259 - (NSString *) backButtonTitle {
5263 - (void) setDelegate:(id)delegate {
5264 [table_ setDelegate:delegate];
5265 [super setDelegate:delegate];
5271 @implementation CYBook
5275 [indicator_ release];
5277 [progress_ release];
5281 - (NSString *) getTitleForPage:(RVPage *)page {
5282 return Simplify([super getTitleForPage:page]);
5290 [navbar_ setPrompt:@""];
5291 [navbar_ addSubview:overlay_];
5292 [indicator_ startAnimation];
5293 [prompt_ setText:@"Updating Database..."];
5294 [progress_ setProgress:0];
5299 detachNewThreadSelector:@selector(_update)
5308 [overlay_ removeFromSuperview];
5309 [indicator_ stopAnimation];
5310 [delegate_ reloadData];
5312 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5315 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5316 if ((self = [super initWithFrame:frame]) != nil) {
5317 database_ = database;
5319 CGRect ovrrect = [navbar_ bounds];
5320 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5322 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5324 bool ugly = [navbar_ _barStyle:NO] == 0;
5326 UIProgressIndicatorStyle style = ugly ?
5327 kUIProgressIndicatorStyleSmallBlack :
5328 kUIProgressIndicatorStyleSmallWhite;
5330 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5331 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5332 CGRect indrect = {{indoffset, indoffset}, indsize};
5334 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5335 [indicator_ setStyle:style];
5336 [overlay_ addSubview:indicator_];
5338 CGSize prmsize = {200, indsize.width + 4};
5341 indoffset * 2 + indsize.width,
5345 (ovrrect.size.height - prmsize.height) / 2
5348 UIFont *font = [UIFont systemFontOfSize:12];
5350 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5352 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : White_)]];
5353 [prompt_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
5354 [prompt_ setFont:font];
5356 [overlay_ addSubview:prompt_];
5358 CGSize prgsize = {75, 100};
5361 ovrrect.size.width - prgsize.width - 10,
5362 (ovrrect.size.height - prgsize.height) / 2
5365 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5366 [progress_ setStyle:0];
5367 [overlay_ addSubview:progress_];
5372 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5375 status.setDelegate(self);
5377 [database_ updateWithStatus:status];
5380 performSelectorOnMainThread:@selector(_update_)
5388 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5389 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5392 - (void) setProgressTitle:(NSString *)title {
5394 performSelectorOnMainThread:@selector(_setProgressTitle:)
5400 - (void) setProgressPercent:(float)percent {
5403 - (void) addProgressOutput:(NSString *)output {
5405 performSelectorOnMainThread:@selector(_addProgressOutput:)
5411 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5415 - (void) _setProgressTitle:(NSString *)title {
5416 [prompt_ setText:[title stringByAppendingString:@"..."]];
5419 - (void) _addProgressOutput:(NSString *)output {
5424 @interface Cydia : UIApplication <
5425 ConfirmationViewDelegate,
5426 ProgressViewDelegate,
5435 UIToolbar *buttonbar_;
5437 ConfirmationView *confirm_;
5439 NSMutableArray *essential_;
5440 NSMutableArray *broken_;
5442 Database *database_;
5443 ProgressView *progress_;
5447 UIKeyboard *keyboard_;
5448 UIProgressHUD *hud_;
5450 InstallView *install_;
5451 ChangesView *changes_;
5452 ManageView *manage_;
5453 SearchView *search_;
5458 @implementation Cydia
5461 if ([broken_ count] != 0) {
5462 int count = [broken_ count];
5464 UIActionSheet *sheet = [[[UIActionSheet alloc]
5465 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5466 buttons:[NSArray arrayWithObjects:
5468 @"Ignore (Temporary)",
5470 defaultButtonIndex:0
5475 [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."];
5476 [sheet popupAlertAnimated:YES];
5477 } else if (!Ignored_ && [essential_ count] != 0) {
5478 int count = [essential_ count];
5480 UIActionSheet *sheet = [[[UIActionSheet alloc]
5481 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5482 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5483 defaultButtonIndex:0
5488 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5489 [sheet popupAlertAnimated:YES];
5493 - (void) _reloadData {
5494 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5495 [hud setText:@"Reloading Data"];
5496 [overlay_ addSubview:hud];
5499 [database_ reloadData];
5503 [essential_ removeAllObjects];
5504 [broken_ removeAllObjects];
5506 NSArray *packages = [database_ packages];
5507 for (int i(0), e([packages count]); i != e; ++i) {
5508 Package *package = [packages objectAtIndex:i];
5510 [broken_ addObject:package];
5511 if ([package upgradableAndEssential:NO]) {
5512 if ([package essential])
5513 [essential_ addObject:package];
5519 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5520 [buttonbar_ setBadgeValue:badge forButton:3];
5521 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5522 [buttonbar_ setBadgeAnimated:YES forButton:3];
5523 [self setApplicationBadge:badge];
5525 [buttonbar_ setBadgeValue:nil forButton:3];
5526 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5527 [buttonbar_ setBadgeAnimated:NO forButton:3];
5528 [self removeApplicationBadge];
5533 if ([packages count] == 0);
5542 [hud removeFromSuperview];*/
5545 - (void) _saveConfig {
5547 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5552 - (void) updateData {
5555 /* XXX: this is just stupid */
5557 [install_ reloadData];
5559 [changes_ reloadData];
5561 [search_ reloadData];
5571 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5572 _assert(file != NULL);
5574 NSArray *keys = [Sources_ allKeys];
5576 for (int i(0), e([keys count]); i != e; ++i) {
5577 NSString *key = [keys objectAtIndex:i];
5578 NSDictionary *source = [Sources_ objectForKey:key];
5580 fprintf(file, "%s %s %s\n",
5581 [[source objectForKey:@"Type"] UTF8String],
5582 [[source objectForKey:@"URI"] UTF8String],
5583 [[source objectForKey:@"Distribution"] UTF8String]
5592 detachNewThreadSelector:@selector(update_)
5595 title:@"Updating Sources..."
5599 - (void) reloadData {
5600 @synchronized (self) {
5601 if (confirm_ == nil)
5607 pkgProblemResolver *resolver = [database_ resolver];
5609 resolver->InstallProtect();
5610 if (!resolver->Resolve(true))
5615 [database_ prepare];
5617 if ([database_ cache]->BrokenCount() == 0)
5618 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5620 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5621 NSArray *packages = [database_ packages];
5623 for (size_t i(0); i != [packages count]; ++i) {
5624 Package *package = [packages objectAtIndex:i];
5625 if ([package broken])
5626 [broken addObject:[package name]];
5629 UIActionSheet *sheet = [[[UIActionSheet alloc]
5630 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5631 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5632 defaultButtonIndex:0
5637 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5638 [sheet popupAlertAnimated:YES];
5644 - (void) installPackage:(Package *)package {
5645 @synchronized (self) {
5652 - (void) removePackage:(Package *)package {
5653 @synchronized (self) {
5660 - (void) distUpgrade {
5661 @synchronized (self) {
5662 [database_ upgrade];
5668 @synchronized (self) {
5676 [overlay_ removeFromSuperview];
5680 detachNewThreadSelector:@selector(perform)
5687 - (void) bootstrap_ {
5689 [database_ upgrade];
5690 [database_ prepare];
5691 [database_ perform];
5694 - (void) bootstrap {
5696 detachNewThreadSelector:@selector(bootstrap_)
5699 title:@"Bootstrap Install..."
5703 - (void) progressViewIsComplete:(ProgressView *)progress {
5704 @synchronized (self) {
5707 if (confirm_ != nil) {
5708 [underlay_ addSubview:overlay_];
5709 [confirm_ removeFromSuperview];
5716 - (void) setPage:(RVPage *)page {
5717 [page resetViewAnimated:NO];
5718 [page setDelegate:self];
5719 [book_ setPage:page];
5722 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5723 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5724 [browser loadURL:url];
5728 - (void) _setHomePage {
5729 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5732 - (void) buttonBarItemTapped:(id)sender {
5733 unsigned tag = [sender tag];
5735 [book_ resetViewAnimated:YES];
5737 } else if (tag_ == 2 && tag != 2)
5738 [install_ resetView];
5741 case 1: [self _setHomePage]; break;
5743 case 2: [self setPage:install_]; break;
5744 case 3: [self setPage:changes_]; break;
5745 case 4: [self setPage:manage_]; break;
5746 case 5: [self setPage:search_]; break;
5748 default: _assert(false);
5754 - (void) fixSpringBoard {
5755 pid_t pid = ExecFork();
5759 if (pid_t child = fork()) {
5760 waitpid(child, NULL, 0);
5762 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5763 perror("launchctl unload");
5767 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5768 perror("launchctl load");
5773 - (void) applicationWillSuspend {
5778 [self fixSpringBoard];
5782 [super applicationWillSuspend];
5785 - (void) askForSettings {
5786 UIActionSheet *role = [[[UIActionSheet alloc]
5787 initWithTitle:@"Who Are You?"
5788 buttons:[NSArray arrayWithObjects:
5789 @"User (Graphical Only)",
5790 @"Hacker (+ Command Line)",
5791 @"Developer (No Filters)",
5793 defaultButtonIndex:-1
5798 [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."];
5799 [role popupAlertAnimated:YES];
5804 [self setStatusBarShowsProgress:NO];
5807 [hud_ removeFromSuperview];
5811 pid_t pid = ExecFork();
5813 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5814 perror("launchctl stop");
5821 [self askForSettings];
5825 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5827 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5828 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5829 0, 0, screenrect.size.width, screenrect.size.height - 48
5830 ) database:database_];
5832 [book_ setDelegate:self];
5834 [overlay_ addSubview:book_];
5836 NSArray *buttonitems = [NSArray arrayWithObjects:
5837 [NSDictionary dictionaryWithObjectsAndKeys:
5838 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5839 @"home-up.png", kUIButtonBarButtonInfo,
5840 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5841 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5842 self, kUIButtonBarButtonTarget,
5843 @"Home", kUIButtonBarButtonTitle,
5844 @"0", kUIButtonBarButtonType,
5847 [NSDictionary dictionaryWithObjectsAndKeys:
5848 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5849 @"install-up.png", kUIButtonBarButtonInfo,
5850 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5851 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5852 self, kUIButtonBarButtonTarget,
5853 @"Sections", kUIButtonBarButtonTitle,
5854 @"0", kUIButtonBarButtonType,
5857 [NSDictionary dictionaryWithObjectsAndKeys:
5858 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5859 @"changes-up.png", kUIButtonBarButtonInfo,
5860 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5861 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5862 self, kUIButtonBarButtonTarget,
5863 @"Changes", kUIButtonBarButtonTitle,
5864 @"0", kUIButtonBarButtonType,
5867 [NSDictionary dictionaryWithObjectsAndKeys:
5868 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5869 @"manage-up.png", kUIButtonBarButtonInfo,
5870 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5871 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5872 self, kUIButtonBarButtonTarget,
5873 @"Manage", kUIButtonBarButtonTitle,
5874 @"0", kUIButtonBarButtonType,
5877 [NSDictionary dictionaryWithObjectsAndKeys:
5878 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5879 @"search-up.png", kUIButtonBarButtonInfo,
5880 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5881 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5882 self, kUIButtonBarButtonTarget,
5883 @"Search", kUIButtonBarButtonTitle,
5884 @"0", kUIButtonBarButtonType,
5888 buttonbar_ = [[UIToolbar alloc]
5890 withFrame:CGRectMake(
5891 0, screenrect.size.height - ButtonBarHeight_,
5892 screenrect.size.width, ButtonBarHeight_
5894 withItemList:buttonitems
5897 [buttonbar_ setDelegate:self];
5898 [buttonbar_ setBarStyle:1];
5899 [buttonbar_ setButtonBarTrackingMode:2];
5901 int buttons[5] = {1, 2, 3, 4, 5};
5902 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5903 [buttonbar_ showButtonGroup:0 withDuration:0];
5905 for (int i = 0; i != 5; ++i)
5906 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5907 i * 64 + 2, 1, 60, ButtonBarHeight_
5910 [buttonbar_ showSelectionForButton:1];
5911 [overlay_ addSubview:buttonbar_];
5913 [UIKeyboard initImplementationNow];
5914 CGSize keysize = [UIKeyboard defaultSize];
5915 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5916 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5917 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5918 [overlay_ addSubview:keyboard_];
5920 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5921 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5922 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5924 manage_ = (ManageView *) [[self
5925 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5926 withClass:[ManageView class]
5930 [underlay_ addSubview:overlay_];
5937 [self _setHomePage];
5940 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5941 NSString *context = [sheet context];
5942 if ([context isEqualToString:@"fixhalf"])
5945 @synchronized (self) {
5946 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5947 Package *broken = [broken_ objectAtIndex:i];
5950 NSString *id = [broken id];
5951 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5952 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5953 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5954 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5963 [broken_ removeAllObjects];
5970 else if ([context isEqualToString:@"role"]) {
5972 case 1: Role_ = @"User"; break;
5973 case 2: Role_ = @"Hacker"; break;
5974 case 3: Role_ = @"Developer"; break;
5981 bool reset = Settings_ != nil;
5983 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
5987 [Metadata_ setObject:Settings_ forKey:@"Settings"];
5995 } else if ([context isEqualToString:@"upgrade"])
5998 @synchronized (self) {
5999 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6000 Package *essential = [essential_ objectAtIndex:i];
6001 [essential install];
6020 - (void) reorganize {
6021 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6022 system("/usr/libexec/cydia/free.sh");
6023 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6027 - (void) applicationSuspend:(__GSEvent *)event {
6028 if (hud_ == nil && ![progress_ isRunning])
6029 [super applicationSuspend:event];
6032 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6034 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6037 - (void) _setSuspended:(BOOL)value {
6039 [super _setSuspended:value];
6042 - (UIProgressHUD *) addProgressHUD {
6043 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6045 [underlay_ addSubview:hud];
6049 - (void) applicationDidFinishLaunching:(id)unused {
6050 Font12_ = [[UIFont systemFontOfSize:12] retain];
6051 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6052 Font14_ = [[UIFont systemFontOfSize:14] retain];
6053 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6054 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6056 _assert(pkgInitConfig(*_config));
6057 _assert(pkgInitSystem(*_config, _system));
6062 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6063 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6065 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6066 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6068 [window_ orderFront:self];
6069 [window_ makeKey:self];
6070 [window_ _setHidden:NO];
6072 database_ = [[Database alloc] init];
6073 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6074 [database_ setDelegate:progress_];
6075 [window_ setContentView:progress_];
6077 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6078 [progress_ setContentView:underlay_];
6080 [progress_ resetView];
6083 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6084 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6085 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6086 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6087 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6088 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
6090 [self setIdleTimerDisabled:YES];
6092 hud_ = [self addProgressHUD];
6093 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6095 [self setStatusBarShowsProgress:YES];
6098 detachNewThreadSelector:@selector(reorganize)
6106 - (void) showKeyboard:(BOOL)show {
6107 CGSize keysize = [UIKeyboard defaultSize];
6108 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6109 CGRect keyup = keydown;
6110 keyup.origin.y -= keysize.height;
6112 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6113 [animation setSignificantRectFields:2];
6116 [animation setStartFrame:keydown];
6117 [animation setEndFrame:keyup];
6118 [keyboard_ activate];
6120 [animation setStartFrame:keyup];
6121 [animation setEndFrame:keydown];
6122 [keyboard_ deactivate];
6125 [[UIAnimator sharedAnimator]
6126 addAnimations:[NSArray arrayWithObjects:animation, nil]
6127 withDuration:KeyboardTime_
6132 - (void) slideUp:(UIActionSheet *)alert {
6134 [alert presentSheetFromButtonBar:buttonbar_];
6136 [alert presentSheetInView:overlay_];
6141 void AddPreferences(NSString *plist) {
6142 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6144 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6145 _assert(settings != NULL);
6146 NSMutableArray *items = [settings objectForKey:@"items"];
6150 for (size_t i(0); i != [items count]; ++i) {
6151 NSMutableDictionary *item([items objectAtIndex:i]);
6152 NSString *label = [item objectForKey:@"label"];
6153 if (label != nil && [label isEqualToString:@"Cydia"]) {
6160 for (size_t i(0); i != [items count]; ++i) {
6161 NSDictionary *item([items objectAtIndex:i]);
6162 NSString *label = [item objectForKey:@"label"];
6163 if (label != nil && [label isEqualToString:@"General"]) {
6164 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6165 @"CydiaSettings", @"bundle",
6166 @"PSLinkCell", @"cell",
6167 [NSNumber numberWithBool:YES], @"hasIcon",
6168 [NSNumber numberWithBool:YES], @"isController",
6170 nil] atIndex:(i + 1)];
6176 _assert([settings writeToFile:plist atomically:YES] == YES);
6183 id Alloc_(id self, SEL selector) {
6184 id object = alloc_(self, selector);
6185 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6190 id Dealloc_(id self, SEL selector) {
6191 id object = dealloc_(self, selector);
6192 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6196 int main(int argc, char *argv[]) {
6197 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6199 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6201 App_ = [[NSBundle mainBundle] bundlePath];
6202 Home_ = NSHomeDirectory();
6205 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6206 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6207 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6208 Sounds_Keyboard_ = [keyboard boolValue];
6214 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6215 _assert(errno == ENOENT);
6216 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6217 _assert(errno == ENOENT);
6219 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6220 alloc_ = alloc->method_imp;
6221 alloc->method_imp = (IMP) &Alloc_;*/
6223 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6224 dealloc_ = dealloc->method_imp;
6225 dealloc->method_imp = (IMP) &Dealloc_;*/
6227 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6228 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6229 Firmware_ = strdup([prover UTF8String]);
6230 NSArray *versions = [prover componentsSeparatedByString:@"."];
6231 int count = [versions count];
6232 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6233 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6234 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6239 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6240 char *machine = new char[size];
6241 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6244 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6245 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6246 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6247 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6251 IOObjectRelease(service);
6254 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6255 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6257 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6258 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6260 Settings_ = [Metadata_ objectForKey:@"Settings"];
6262 Packages_ = [Metadata_ objectForKey:@"Packages"];
6263 Sections_ = [Metadata_ objectForKey:@"Sections"];
6264 Sources_ = [Metadata_ objectForKey:@"Sources"];
6267 if (Settings_ != nil)
6268 Role_ = [Settings_ objectForKey:@"Role"];
6270 if (Packages_ == nil) {
6271 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6272 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6275 if (Sections_ == nil) {
6276 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6277 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6280 if (Sources_ == nil) {
6281 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6282 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6285 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6286 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6288 if (access("/User", F_OK) != 0)
6289 system("/usr/libexec/cydia/firmware.sh");
6291 Locale_ = CFLocaleCopyCurrent();
6292 space_ = CGColorSpaceCreateDeviceRGB();
6294 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6295 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6296 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6297 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6298 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6299 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6300 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6302 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6304 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6306 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6308 CGColorSpaceRelease(space_);