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>
45 #include <WebCore/DOMHTML.h>
47 #import <UIKit/UIActionSheet.h>
48 #import <UIKit/UIAnimator.h>
49 #import <UIKit/UIApplication.h>
50 #import <UIKit/UIColor.h>
51 #import <UIKit/UIFieldEditor.h>
52 #import <UIKit/UIFrameAnimation.h>
53 #import <UIKit/UIHardware.h>
54 #import <UIKit/UIImage.h>
55 #import <UIKit/UIImageAndTextTableCell.h>
56 #import <UIKit/UIImageView.h>
57 #import <UIKit/UIKeyboard.h>
58 #import <UIKit/UIKeyboardImpl.h>
59 #import <UIKit/UINavigationBar.h>
60 #import <UIKit/UINavigationItem.h>
61 #import <UIKit/UIPreferencesTable.h>
62 #import <UIKit/UIPreferencesTableCell.h>
63 #import <UIKit/UIProgressBar.h>
64 #import <UIKit/UIProgressHUD.h>
65 #import <UIKit/UIProgressIndicator.h>
66 #import <UIKit/UIPushButton.h>
67 #import <UIKit/UISearchField.h>
68 #import <UIKit/UISimpleTableCell.h>
69 #import <UIKit/_UISwitchSlider.h>
70 #import <UIKit/UITableCell.h>
71 #import <UIKit/UITableColumn.h>
72 #import <UIKit/UITextField.h>
73 #import <UIKit/UITextInputTraits.h>
74 #import <UIKit/UITextLabel.h>
75 #import <UIKit/UITextView.h>
76 #import <UIKit/UIToolbar.h>
77 #import <UIKit/UITransitionView.h>
78 #import <UIKit/UIWebDocumentView.h>
79 #import <UIKit/UIWebView.h>
80 #import <UIKit/UIWindow.h>
82 #import <UIKit/UIView-Geometry.h>
83 #import <UIKit/UIView-Hierarchy.h>
84 #import <UIKit/UIView-Rendering.h>
86 #import <UIKit/NSString-UIStringDrawing.h>
89 #import <UIKit/UIActionSheet-Private.h>
90 #import <UIKit/UIControl-UIControlPrivate.h>
91 #import <UIKit/UIImage-UIImageDeprecated.h>
92 #import <UIKit/UIImage-UIImagePrivate.h>
93 #import <UIKit/UINavigationBar-Static.h>
94 #import <UIKit/UIProgressHUD-Deprecated.h>
95 #import <UIKit/UIToolbar-UIButtonBarPrivate.h>
96 #import <UIKit/UIView-Deprecated.h>
97 #import <UIKit/UIWindow-Static.h>
100 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
102 #include <WebKit/WebFrame.h>
103 #include <WebKit/WebView.h>
108 #include <ext/stdio_filebuf.h>
110 #include <apt-pkg/acquire.h>
111 #include <apt-pkg/acquire-item.h>
112 #include <apt-pkg/algorithms.h>
113 #include <apt-pkg/cachefile.h>
114 #include <apt-pkg/clean.h>
115 #include <apt-pkg/configuration.h>
116 #include <apt-pkg/debmetaindex.h>
117 #include <apt-pkg/error.h>
118 #include <apt-pkg/init.h>
119 #include <apt-pkg/mmap.h>
120 #include <apt-pkg/pkgrecords.h>
121 #include <apt-pkg/sha1.h>
122 #include <apt-pkg/sourcelist.h>
123 #include <apt-pkg/sptr.h>
125 #include <sys/sysctl.h>
130 #include <mach-o/nlist.h>
140 #define UIWebView UIWebDocumentView
142 #import "BrowserView.h"
143 #import "ResetView.h"
144 #import "UICaboodle.h"
147 /* iPhoneOS 2.0 Compatibility {{{ */
149 @interface UICGColor : NSObject {
152 - (id) initWithCGColor:(CGColorRef)color;
158 + (id)systemFontOfSize:(float)fp8;
159 + (id)boldSystemFontOfSize:(float)fp8;
160 - (UIFont *) fontWithSize:(CGFloat)size;
163 @interface NSObject (iPhoneOS)
164 - (CGColorRef) cgColor;
165 - (CGColorRef) CGColor;
169 @implementation NSObject (iPhoneOS)
171 - (CGColorRef) cgColor {
172 return [self CGColor];
175 - (CGColorRef) CGColor {
176 return (CGColorRef) self;
180 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
185 @interface UITextView (iPhoneOS)
186 - (void) setTextSize:(float)size;
189 @implementation UITextView (iPhoneOS)
191 - (void) setTextSize:(float)size {
192 [self setFont:[[self font] fontWithSize:size]];
199 @interface UIApplication (IdleTimer)
200 - (void) setIdleTimerDisabled:(char)arg0;
203 extern "C" int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
205 extern NSString *kUIButtonBarButtonAction;
206 extern NSString *kUIButtonBarButtonInfo;
207 extern NSString *kUIButtonBarButtonInfoOffset;
208 extern NSString *kUIButtonBarButtonSelectedInfo;
209 extern NSString *kUIButtonBarButtonStyle;
210 extern NSString *kUIButtonBarButtonTag;
211 extern NSString *kUIButtonBarButtonTarget;
212 extern NSString *kUIButtonBarButtonTitle;
213 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
214 extern NSString *kUIButtonBarButtonTitleWidth;
215 extern NSString *kUIButtonBarButtonType;
218 kUIProgressIndicatorStyleLargeWhite = 0,
219 kUIProgressIndicatorStyleMediumWhite = 1,
220 kUIProgressIndicatorStyleSmallWhite = 3,
221 kUIProgressIndicatorStyleSmallBlack = 4
222 } UIProgressIndicatorStyle;
225 kUIControlEventMouseDown = 1 << 0,
226 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
227 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
228 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
229 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
230 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
231 } UIControlEventMasks;
233 @interface NSString (UIKit)
234 - (NSString *) stringByAddingPercentEscapes;
235 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
238 @interface NSString (Cydia)
239 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
240 - (NSComparisonResult) compareByPath:(NSString *)other;
243 @implementation NSString (Cydia)
245 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
246 char data[length + 1];
247 memcpy(data, bytes, length);
249 return [NSString stringWithUTF8String:data];
252 - (NSComparisonResult) compareByPath:(NSString *)other {
253 NSString *prefix = [self commonPrefixWithString:other options:0];
254 size_t length = [prefix length];
256 NSRange lrange = NSMakeRange(length, [self length] - length);
257 NSRange rrange = NSMakeRange(length, [other length] - length);
259 lrange = [self rangeOfString:@"/" options:0 range:lrange];
260 rrange = [other rangeOfString:@"/" options:0 range:rrange];
262 NSComparisonResult value;
264 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
265 value = NSOrderedSame;
266 else if (lrange.location == NSNotFound)
267 value = NSOrderedAscending;
268 else if (rrange.location == NSNotFound)
269 value = NSOrderedDescending;
271 value = NSOrderedSame;
273 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
274 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
275 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
276 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
278 NSComparisonResult result = [lpath compare:rpath];
279 return result == NSOrderedSame ? value : result;
284 /* Perl-Compatible RegEx {{{ */
294 Pcre(const char *regex) :
299 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
302 fprintf(stderr, "%d:%s\n", offset, error);
306 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
307 matches_ = new int[(capture_ + 1) * 3];
315 NSString *operator [](size_t match) {
316 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
319 bool operator ()(NSString *data) {
320 // XXX: length is for characters, not for bytes
321 return operator ()([data UTF8String], [data length]);
324 bool operator ()(const char *data, size_t size) {
326 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
330 /* Mime Addresses {{{ */
331 @interface Address : NSObject {
337 - (NSString *) address;
339 + (Address *) addressWithString:(NSString *)string;
340 - (Address *) initWithString:(NSString *)string;
343 @implementation Address
352 - (NSString *) name {
356 - (NSString *) address {
360 + (Address *) addressWithString:(NSString *)string {
361 return [[[Address alloc] initWithString:string] autorelease];
364 + (NSArray *) _attributeKeys {
365 return [NSArray arrayWithObjects:@"address", @"name", nil];
368 - (NSArray *) attributeKeys {
369 return [[self class] _attributeKeys];
372 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
373 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
376 - (Address *) initWithString:(NSString *)string {
377 if ((self = [super init]) != nil) {
378 const char *data = [string UTF8String];
379 size_t size = [string length];
381 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
383 if (address_r(data, size)) {
384 name_ = [address_r[1] retain];
385 address_ = [address_r[2] retain];
387 name_ = [[NSString alloc]
390 encoding:kCFStringEncodingUTF8
400 /* CoreGraphics Primitives {{{ */
411 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
414 Set(space, red, green, blue, alpha);
419 CGColorRelease(color_);
426 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
428 float color[] = {red, green, blue, alpha};
429 color_ = CGColorCreate(space, color);
432 operator CGColorRef() {
448 extern "C" void UISetColor(CGColorRef color);
450 /* Random Global Variables {{{ */
451 static const int PulseInterval_ = 50000;
452 static const int ButtonBarHeight_ = 48;
453 static const float KeyboardTime_ = 0.3f;
454 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
456 static CGColor Blue_;
457 static CGColor Blueish_;
458 static CGColor Black_;
459 static CGColor Clear_;
461 static CGColor White_;
462 static CGColor Gray_;
464 static NSString *Home_;
465 static BOOL Sounds_Keyboard_;
467 static BOOL Advanced_;
469 static BOOL Ignored_;
471 static UIFont *Font12_;
472 static UIFont *Font12Bold_;
473 static UIFont *Font14_;
474 static UIFont *Font18Bold_;
475 static UIFont *Font22Bold_;
477 const char *Firmware_ = NULL;
478 const char *Machine_ = NULL;
479 const char *SerialNumber_ = NULL;
486 CGColorSpaceRef space_;
488 #define FW_LEAST(major, minor, bugfix) \
489 (major < Major_ || major == Major_ && \
490 (minor < Minor_ || minor == Minor_ && \
496 static NSDictionary *SectionMap_;
497 static NSMutableDictionary *Metadata_;
498 static _transient NSMutableDictionary *Settings_;
499 static _transient NSString *Role_;
500 static _transient NSMutableDictionary *Packages_;
501 static _transient NSMutableDictionary *Sections_;
502 static _transient NSMutableDictionary *Sources_;
503 static bool Changed_;
506 NSString *GetLastUpdate() {
507 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
510 return @"Never or Unknown";
512 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
513 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
515 CFRelease(formatter);
517 return [(NSString *) formatted autorelease];
520 /* Display Helpers {{{ */
521 inline float Interpolate(float begin, float end, float fraction) {
522 return (end - begin) * fraction + begin;
525 NSString *SizeString(double size) {
527 while (size > 1024) {
532 static const char *powers_[] = {"B", "kB", "MB", "GB"};
534 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
537 NSString *StripVersion(NSString *version) {
538 NSRange colon = [version rangeOfString:@":"];
539 if (colon.location != NSNotFound)
540 version = [version substringFromIndex:(colon.location + 1)];
544 static const float TextViewOffset_ = 22;
546 UITextView *GetTextView(NSString *value, float left, bool html) {
547 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
548 [text setEditable:NO];
549 [text setTextSize:16];
551 [text setHTML:value];
553 [text setText:value];
554 [text setEnabled:NO];
556 [text setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
558 CGRect frame = [text frame];
559 [text setFrame:frame];
560 CGRect rect = [text visibleTextRect];
561 frame.size.height = rect.size.height;
562 [text setFrame:frame];
567 NSString *Simplify(NSString *title) {
568 const char *data = [title UTF8String];
569 size_t size = [title length];
571 static Pcre square_r("^\\[(.*)\\]$");
572 if (square_r(data, size))
573 return Simplify(square_r[1]);
575 static Pcre paren_r("^\\((.*)\\)$");
576 if (paren_r(data, size))
577 return Simplify(paren_r[1]);
579 static Pcre title_r("^(.*?) \\(.*\\)$");
580 if (title_r(data, size))
581 return Simplify(title_r[1]);
587 bool isSectionVisible(NSString *section) {
588 NSDictionary *metadata = [Sections_ objectForKey:section];
589 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
590 return hidden == nil || ![hidden boolValue];
593 /* Delegate Prototypes {{{ */
597 @interface NSObject (ProgressDelegate)
600 @implementation NSObject(ProgressDelegate)
602 - (void) _setProgressError:(NSArray *)args {
603 [self performSelector:@selector(setProgressError:forPackage:)
604 withObject:[args objectAtIndex:0]
605 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
611 @protocol ProgressDelegate
612 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
613 - (void) setProgressTitle:(NSString *)title;
614 - (void) setProgressPercent:(float)percent;
615 - (void) addProgressOutput:(NSString *)output;
618 @protocol ConfigurationDelegate
619 - (void) repairWithSelector:(SEL)selector;
620 - (void) setConfigurationData:(NSString *)data;
623 @protocol CydiaDelegate
624 - (void) installPackage:(Package *)package;
625 - (void) removePackage:(Package *)package;
626 - (void) slideUp:(UIActionSheet *)alert;
627 - (void) distUpgrade;
630 - (void) askForSettings;
631 - (UIProgressHUD *) addProgressHUD;
635 /* Status Delegation {{{ */
637 public pkgAcquireStatus
640 _transient NSObject<ProgressDelegate> *delegate_;
648 void setDelegate(id delegate) {
649 delegate_ = delegate;
652 virtual bool MediaChange(std::string media, std::string drive) {
656 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
659 virtual void Fetch(pkgAcquire::ItemDesc &item) {
660 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
663 virtual void Done(pkgAcquire::ItemDesc &item) {
666 virtual void Fail(pkgAcquire::ItemDesc &item) {
668 item.Owner->Status == pkgAcquire::Item::StatIdle ||
669 item.Owner->Status == pkgAcquire::Item::StatDone
673 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
674 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
679 virtual bool Pulse(pkgAcquire *Owner) {
680 bool value = pkgAcquireStatus::Pulse(Owner);
683 double(CurrentBytes + CurrentItems) /
684 double(TotalBytes + TotalItems)
687 [delegate_ setProgressPercent:percent];
691 virtual void Start() {
694 virtual void Stop() {
698 /* Progress Delegation {{{ */
703 _transient id<ProgressDelegate> delegate_;
706 virtual void Update() {
707 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
708 [delegate_ setProgressPercent:(Percent / 100)];
717 void setDelegate(id delegate) {
718 delegate_ = delegate;
721 virtual void Done() {
722 [delegate_ setProgressPercent:1];
727 /* Database Interface {{{ */
728 @interface Database : NSObject {
730 pkgDepCache::Policy *policy_;
731 pkgRecords *records_;
732 pkgProblemResolver *resolver_;
733 pkgAcquire *fetcher_;
735 SPtr<pkgPackageManager> manager_;
736 pkgSourceList *list_;
738 NSMutableDictionary *sources_;
739 NSMutableArray *packages_;
741 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
750 - (void) _readCydia:(NSNumber *)fd;
751 - (void) _readStatus:(NSNumber *)fd;
752 - (void) _readOutput:(NSNumber *)fd;
756 - (Package *) packageWithName:(NSString *)name;
759 - (pkgCacheFile &) cache;
760 - (pkgDepCache::Policy *) policy;
761 - (pkgRecords *) records;
762 - (pkgProblemResolver *) resolver;
763 - (pkgAcquire &) fetcher;
764 - (NSArray *) packages;
765 - (NSArray *) sources;
774 - (void) updateWithStatus:(Status &)status;
776 - (void) setDelegate:(id)delegate;
777 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
781 /* Source Class {{{ */
782 @interface Source : NSObject {
783 NSString *description_;
788 NSString *distribution_;
792 NSString *defaultIcon_;
794 NSDictionary *record_;
798 - (Source *) initWithMetaIndex:(metaIndex *)index;
800 - (NSComparisonResult) compareByNameAndType:(Source *)source;
802 - (NSDictionary *) record;
806 - (NSString *) distribution;
812 - (NSString *) description;
813 - (NSString *) label;
814 - (NSString *) origin;
815 - (NSString *) version;
817 - (NSString *) defaultIcon;
821 @implementation Source
825 [distribution_ release];
828 if (description_ != nil)
829 [description_ release];
836 if (defaultIcon_ != nil)
837 [defaultIcon_ release];
844 + (NSArray *) _attributeKeys {
845 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
848 - (NSArray *) attributeKeys {
849 return [[self class] _attributeKeys];
852 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
853 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
856 - (Source *) initWithMetaIndex:(metaIndex *)index {
857 if ((self = [super init]) != nil) {
858 trusted_ = index->IsTrusted();
860 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
861 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
862 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
864 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
865 if (dindex != NULL) {
866 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
868 while (std::getline(release, line)) {
869 std::string::size_type colon(line.find(':'));
870 if (colon == std::string::npos)
873 std::string name(line.substr(0, colon));
874 std::string value(line.substr(colon + 1));
875 while (!value.empty() && value[0] == ' ')
876 value = value.substr(1);
878 if (name == "Default-Icon")
879 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
880 else if (name == "Description")
881 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
882 else if (name == "Label")
883 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
884 else if (name == "Origin")
885 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
886 else if (name == "Version")
887 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
891 record_ = [Sources_ objectForKey:[self key]];
893 record_ = [record_ retain];
897 - (NSComparisonResult) compareByNameAndType:(Source *)source {
898 NSDictionary *lhr = [self record];
899 NSDictionary *rhr = [source record];
902 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
904 NSString *lhs = [self name];
905 NSString *rhs = [source name];
907 if ([lhs length] != 0 && [rhs length] != 0) {
908 unichar lhc = [lhs characterAtIndex:0];
909 unichar rhc = [rhs characterAtIndex:0];
911 if (isalpha(lhc) && !isalpha(rhc))
912 return NSOrderedAscending;
913 else if (!isalpha(lhc) && isalpha(rhc))
914 return NSOrderedDescending;
917 return [lhs caseInsensitiveCompare:rhs];
920 - (NSDictionary *) record {
932 - (NSString *) distribution {
933 return distribution_;
936 - (NSString *) type {
941 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
944 - (NSString *) host {
945 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
948 - (NSString *) name {
949 return origin_ == nil ? [self host] : origin_;
952 - (NSString *) description {
956 - (NSString *) label {
957 return label_ == nil ? [self host] : label_;
960 - (NSString *) origin {
964 - (NSString *) version {
968 - (NSString *) defaultIcon {
974 /* Relationship Class {{{ */
975 @interface Relationship : NSObject {
986 @implementation Relationship
994 - (NSString *) type {
1002 - (NSString *) name {
1009 /* Package Class {{{ */
1010 NSString *Scour(const char *field, const char *begin, const char *end) {
1011 size_t i(0), l(strlen(field));
1014 const char *name = begin + i;
1015 const char *colon = name + l;
1016 const char *value = colon + 1;
1021 memcmp(name, field, l) == 0
1023 while (value != end && value[0] == ' ')
1025 const char *line = std::find(value, end, '\n');
1026 while (line != value && line[-1] == ' ')
1029 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1031 begin = std::find(begin, end, '\n');
1039 @interface Package : NSObject {
1040 pkgCache::PkgIterator iterator_;
1041 _transient Database *database_;
1042 pkgCache::VerIterator version_;
1043 pkgCache::VerFileIterator file_;
1049 NSString *installed_;
1055 NSString *homepage_;
1056 NSString *depiction_;
1062 NSArray *relationships_;
1065 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1066 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1068 - (pkgCache::PkgIterator) iterator;
1070 - (NSString *) section;
1071 - (Address *) maintainer;
1073 - (NSString *) description;
1074 - (NSString *) index;
1078 - (NSString *) latest;
1079 - (NSString *) installed;
1082 - (BOOL) upgradableAndEssential:(BOOL)essential;
1088 - (BOOL) halfConfigured;
1089 - (BOOL) halfInstalled;
1091 - (NSString *) mode;
1094 - (NSString *) name;
1095 - (NSString *) tagline;
1096 - (NSString *) icon;
1097 - (NSString *) homepage;
1098 - (NSString *) depiction;
1099 - (Address *) author;
1101 - (NSArray *) relationships;
1103 - (Source *) source;
1104 - (NSString *) role;
1106 - (BOOL) matches:(NSString *)text;
1108 - (bool) hasSupportingRole;
1109 - (BOOL) hasTag:(NSString *)tag;
1111 - (NSComparisonResult) compareByName:(Package *)package;
1112 - (NSComparisonResult) compareBySection:(Package *)package;
1113 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1114 - (NSComparisonResult) compareForChanges:(Package *)package;
1119 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search;
1120 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1121 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1122 - (NSNumber *) isVisibleInSource:(Source *)source;
1126 @implementation Package
1133 if (installed_ != nil)
1134 [installed_ release];
1142 if (homepage_ != nil)
1143 [homepage_ release];
1144 if (depiction_ != nil)
1145 [depiction_ release];
1146 if (sponsor_ != nil)
1155 if (relationships_ != nil)
1156 [relationships_ release];
1161 + (NSArray *) _attributeKeys {
1162 return [NSArray arrayWithObjects:@"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"section", @"size", @"source", @"sponsor", @"tagline", nil];
1165 - (NSArray *) attributeKeys {
1166 return [[self class] _attributeKeys];
1169 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1170 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1173 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1174 if ((self = [super init]) != nil) {
1175 iterator_ = iterator;
1176 database_ = database;
1178 version_ = [database_ policy]->GetCandidateVer(iterator_);
1179 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1181 if (!version_.end())
1182 file_ = version_.FileList();
1184 pkgCache &cache([database_ cache]);
1185 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1188 pkgCache::VerIterator current = iterator_.CurrentVer();
1189 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1191 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1194 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1196 const char *begin, *end;
1197 parser->GetRec(begin, end);
1199 name_ = Scour("Name", begin, end);
1201 name_ = [name_ retain];
1202 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1203 icon_ = Scour("Icon", begin, end);
1205 icon_ = [icon_ retain];
1206 homepage_ = Scour("Homepage", begin, end);
1207 if (homepage_ == nil)
1208 homepage_ = Scour("Website", begin, end);
1209 if (homepage_ != nil)
1210 homepage_ = [homepage_ retain];
1211 depiction_ = Scour("Depiction", begin, end);
1212 if (depiction_ != nil)
1213 depiction_ = [depiction_ retain];
1214 NSString *sponsor = Scour("Sponsor", begin, end);
1216 sponsor_ = [[Address addressWithString:sponsor] retain];
1217 NSString *author = Scour("Author", begin, end);
1219 author_ = [[Address addressWithString:author] retain];
1220 NSString *tags = Scour("Tag", begin, end);
1222 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1226 for (int i(0), e([tags_ count]); i != e; ++i) {
1227 NSString *tag = [tags_ objectAtIndex:i];
1228 if ([tag hasPrefix:@"role::"]) {
1229 role_ = [[tag substringFromIndex:6] retain];
1234 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1235 if (metadata == nil || [metadata count] == 0) {
1236 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1240 [Packages_ setObject:metadata forKey:id_];
1246 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1247 return [[[Package alloc]
1248 initWithIterator:iterator
1253 - (pkgCache::PkgIterator) iterator {
1257 - (NSString *) section {
1258 const char *section = iterator_.Section();
1259 if (section == NULL)
1262 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1265 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1266 if (NSString *rename = [value objectForKey:@"Rename"]) {
1271 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1274 - (Address *) maintainer {
1277 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1278 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1282 return version_.end() ? 0 : version_->InstalledSize;
1285 - (NSString *) description {
1288 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1289 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1291 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1292 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1293 if ([lines count] < 2)
1296 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1297 for (size_t i(1); i != [lines count]; ++i) {
1298 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1299 [trimmed addObject:trim];
1302 return [trimmed componentsJoinedByString:@"\n"];
1305 - (NSString *) index {
1306 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1307 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1311 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1314 - (NSString *) latest {
1318 - (NSString *) installed {
1323 return !version_.end();
1326 - (BOOL) upgradableAndEssential:(BOOL)essential {
1327 pkgCache::VerIterator current = iterator_.CurrentVer();
1330 return essential && [self essential];
1332 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1333 return !candidate.end() && candidate != current;
1337 - (BOOL) essential {
1338 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1342 return [database_ cache][iterator_].InstBroken();
1346 NSString *section = [self section];
1347 return [self hasSupportingRole] && (section == nil || isSectionVisible(section));
1351 unsigned char current = iterator_->CurrentState;
1352 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1355 - (BOOL) halfConfigured {
1356 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1359 - (BOOL) halfInstalled {
1360 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1364 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1365 return state.Mode != pkgDepCache::ModeKeep;
1368 - (NSString *) mode {
1369 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1371 switch (state.Mode) {
1372 case pkgDepCache::ModeDelete:
1373 if ((state.iFlags & pkgDepCache::Purge) != 0)
1378 case pkgDepCache::ModeKeep:
1379 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1384 case pkgDepCache::ModeInstall:
1385 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1386 return @"Reinstall";
1387 else switch (state.Status) {
1389 return @"Downgrade";
1395 return @"New Install";
1408 - (NSString *) name {
1409 return name_ == nil ? id_ : name_;
1412 - (NSString *) tagline {
1416 - (NSString *) icon {
1420 - (NSString *) homepage {
1424 - (NSString *) depiction {
1428 - (Address *) sponsor {
1432 - (Address *) author {
1436 - (NSArray *) relationships {
1437 return relationships_;
1440 - (Source *) source {
1442 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1449 - (NSString *) role {
1453 - (BOOL) matches:(NSString *)text {
1459 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1460 if (range.location != NSNotFound)
1463 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1464 if (range.location != NSNotFound)
1467 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1468 if (range.location != NSNotFound)
1474 - (bool) hasSupportingRole {
1477 if ([role_ isEqualToString:@"enduser"])
1479 if ([Role_ isEqualToString:@"User"])
1481 if ([role_ isEqualToString:@"hacker"])
1483 if ([Role_ isEqualToString:@"Hacker"])
1485 if ([role_ isEqualToString:@"developer"])
1487 if ([Role_ isEqualToString:@"Developer"])
1492 - (BOOL) hasTag:(NSString *)tag {
1493 return tags_ == nil ? NO : [tags_ containsObject:tag];
1496 - (NSComparisonResult) compareByName:(Package *)package {
1497 NSString *lhs = [self name];
1498 NSString *rhs = [package name];
1500 if ([lhs length] != 0 && [rhs length] != 0) {
1501 unichar lhc = [lhs characterAtIndex:0];
1502 unichar rhc = [rhs characterAtIndex:0];
1504 if (isalpha(lhc) && !isalpha(rhc))
1505 return NSOrderedAscending;
1506 else if (!isalpha(lhc) && isalpha(rhc))
1507 return NSOrderedDescending;
1510 return [lhs caseInsensitiveCompare:rhs];
1513 - (NSComparisonResult) compareBySection:(Package *)package {
1514 NSString *lhs = [self section];
1515 NSString *rhs = [package section];
1517 if (lhs == NULL && rhs != NULL)
1518 return NSOrderedAscending;
1519 else if (lhs != NULL && rhs == NULL)
1520 return NSOrderedDescending;
1521 else if (lhs != NULL && rhs != NULL) {
1522 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1523 if (result != NSOrderedSame)
1527 return NSOrderedSame;
1530 - (NSComparisonResult) compareBySectionAndName:(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 compare:rhs];
1540 if (result != NSOrderedSame)
1544 return [self compareByName:package];
1547 - (NSComparisonResult) compareForChanges:(Package *)package {
1548 BOOL lhs = [self upgradableAndEssential:YES];
1549 BOOL rhs = [package upgradableAndEssential:YES];
1552 return lhs ? NSOrderedAscending : NSOrderedDescending;
1554 switch ([[self seen] compare:[package seen]]) {
1555 case NSOrderedAscending:
1556 return NSOrderedDescending;
1559 case NSOrderedDescending:
1560 return NSOrderedAscending;
1566 return [self compareByName:package];
1570 pkgProblemResolver *resolver = [database_ resolver];
1571 resolver->Clear(iterator_);
1572 resolver->Protect(iterator_);
1573 pkgCacheFile &cache([database_ cache]);
1574 cache->MarkInstall(iterator_, false);
1575 pkgDepCache::StateCache &state((*cache)[iterator_]);
1576 if (!state.Install())
1577 cache->SetReInstall(iterator_, true);
1581 pkgProblemResolver *resolver = [database_ resolver];
1582 resolver->Clear(iterator_);
1583 resolver->Protect(iterator_);
1584 resolver->Remove(iterator_);
1585 [database_ cache]->MarkDelete(iterator_, true);
1588 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search {
1589 return [NSNumber numberWithBool:(
1590 [self valid] && [self visible] && [self matches:search]
1594 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1595 return [NSNumber numberWithBool:(
1596 (![number boolValue] || [self visible]) && [self installed] != nil
1600 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1601 NSString *section = [self section];
1603 return [NSNumber numberWithBool:(
1604 [self valid] && [self visible] &&
1605 [self installed] == nil && (
1607 section == nil && [name length] == 0 ||
1608 [name isEqualToString:section]
1613 - (NSNumber *) isVisibleInSource:(Source *)source {
1614 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1619 /* Section Class {{{ */
1620 @interface Section : NSObject {
1626 - (NSComparisonResult) compareByName:(Section *)section;
1627 - (Section *) initWithName:(NSString *)name;
1628 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1629 - (NSString *) name;
1632 - (void) addToCount;
1636 @implementation Section
1643 - (NSComparisonResult) compareByName:(Section *)section {
1644 NSString *lhs = [self name];
1645 NSString *rhs = [section name];
1647 if ([lhs length] != 0 && [rhs length] != 0) {
1648 unichar lhc = [lhs characterAtIndex:0];
1649 unichar rhc = [rhs characterAtIndex:0];
1651 if (isalpha(lhc) && !isalpha(rhc))
1652 return NSOrderedAscending;
1653 else if (!isalpha(lhc) && isalpha(rhc))
1654 return NSOrderedDescending;
1657 return [lhs caseInsensitiveCompare:rhs];
1660 - (Section *) initWithName:(NSString *)name {
1661 return [self initWithName:name row:0];
1664 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1665 if ((self = [super init]) != nil) {
1666 name_ = [name retain];
1671 - (NSString *) name {
1683 - (void) addToCount {
1693 /* Database Implementation {{{ */
1694 @implementation Database
1701 - (void) _readCydia:(NSNumber *)fd {
1702 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1704 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1705 std::istream is(&ib);
1708 static Pcre finish_r("^finish:([^:]*)$");
1710 while (std::getline(is, line)) {
1711 const char *data(line.c_str());
1712 size_t size = line.size();
1713 fprintf(stderr, "C:%s\n", data);
1715 if (finish_r(data, size)) {
1716 NSString *finish = finish_r[1];
1717 int index = [Finishes_ indexOfObject:finish];
1718 if (index != INT_MAX && index > Finish_)
1727 - (void) _readStatus:(NSNumber *)fd {
1728 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1730 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1731 std::istream is(&ib);
1734 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1735 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1737 while (std::getline(is, line)) {
1738 const char *data(line.c_str());
1739 size_t size = line.size();
1740 fprintf(stderr, "S:%s\n", data);
1742 if (conffile_r(data, size)) {
1743 [delegate_ setConfigurationData:conffile_r[1]];
1744 } else if (strncmp(data, "status: ", 8) == 0) {
1745 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1746 [delegate_ setProgressTitle:string];
1747 } else if (pmstatus_r(data, size)) {
1748 std::string type([pmstatus_r[1] UTF8String]);
1749 NSString *id = pmstatus_r[2];
1751 float percent([pmstatus_r[3] floatValue]);
1752 [delegate_ setProgressPercent:(percent / 100)];
1754 NSString *string = pmstatus_r[4];
1756 if (type == "pmerror")
1757 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1758 withObject:[NSArray arrayWithObjects:string, id, nil]
1761 else if (type == "pmstatus")
1762 [delegate_ setProgressTitle:string];
1763 else if (type == "pmconffile")
1764 [delegate_ setConfigurationData:string];
1765 else _assert(false);
1766 } else _assert(false);
1773 - (void) _readOutput:(NSNumber *)fd {
1774 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1776 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1777 std::istream is(&ib);
1780 while (std::getline(is, line)) {
1781 fprintf(stderr, "O:%s\n", line.c_str());
1782 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1793 - (Package *) packageWithName:(NSString *)name {
1794 if (static_cast<pkgDepCache *>(cache_) == NULL)
1796 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1797 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1800 - (Database *) init {
1801 if ((self = [super init]) != nil) {
1808 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1809 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1813 _assert(pipe(fds) != -1);
1816 _config->Set("APT::Keep-Fds::", cydiafd_);
1817 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1820 detachNewThreadSelector:@selector(_readCydia:)
1822 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1825 _assert(pipe(fds) != -1);
1829 detachNewThreadSelector:@selector(_readStatus:)
1831 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1834 _assert(pipe(fds) != -1);
1835 _assert(dup2(fds[0], 0) != -1);
1836 _assert(close(fds[0]) != -1);
1838 input_ = fdopen(fds[1], "a");
1840 _assert(pipe(fds) != -1);
1841 _assert(dup2(fds[1], 1) != -1);
1842 _assert(close(fds[1]) != -1);
1845 detachNewThreadSelector:@selector(_readOutput:)
1847 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1852 - (pkgCacheFile &) cache {
1856 - (pkgDepCache::Policy *) policy {
1860 - (pkgRecords *) records {
1864 - (pkgProblemResolver *) resolver {
1868 - (pkgAcquire &) fetcher {
1872 - (NSArray *) packages {
1876 - (NSArray *) sources {
1877 return [sources_ allValues];
1880 - (void) reloadData {
1899 if (!cache_.Open(progress_, true)) {
1901 if (!_error->PopMessage(error))
1904 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1906 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1907 [delegate_ repairWithSelector:@selector(configure)];
1908 else if (error == "The package lists or status file could not be parsed or opened.")
1909 [delegate_ repairWithSelector:@selector(update)];
1910 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1911 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1912 // else if (error == "The list of sources could not be read.")
1913 else _assert(false);
1918 now_ = [[NSDate date] retain];
1920 policy_ = new pkgDepCache::Policy();
1921 records_ = new pkgRecords(cache_);
1922 resolver_ = new pkgProblemResolver(cache_);
1923 fetcher_ = new pkgAcquire(&status_);
1926 list_ = new pkgSourceList();
1927 _assert(list_->ReadMainList());
1929 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1930 _assert(pkgApplyStatus(cache_));
1932 if (cache_->BrokenCount() != 0) {
1933 _assert(pkgFixBroken(cache_));
1934 _assert(cache_->BrokenCount() == 0);
1935 _assert(pkgMinimizeUpgrade(cache_));
1938 [sources_ removeAllObjects];
1939 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1940 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1941 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1943 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1944 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1948 [packages_ removeAllObjects];
1949 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1950 if (Package *package = [Package packageWithIterator:iterator database:self])
1951 [packages_ addObject:package];
1953 [packages_ sortUsingSelector:@selector(compareByName:)];
1956 - (void) configure {
1957 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1958 system([dpkg UTF8String]);
1966 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1967 _assert(!_error->PendingError());
1970 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1973 public pkgArchiveCleaner
1976 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1981 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1983 while (_error->PopMessage(error))
1984 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1989 pkgRecords records(cache_);
1991 lock_ = new FileFd();
1992 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1993 _assert(!_error->PendingError());
1996 // XXX: explain this with an error message
1997 _assert(list.ReadMainList());
1999 manager_ = (_system->CreatePM(cache_));
2000 _assert(manager_->GetArchives(fetcher_, &list, &records));
2001 _assert(!_error->PendingError());
2005 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2007 _assert(list.ReadMainList());
2008 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2009 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2012 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2017 bool failed = false;
2018 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2019 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2022 std::string uri = (*item)->DescURI();
2023 std::string error = (*item)->ErrorText;
2025 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
2028 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2029 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2040 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2042 if (_error->PendingError()) {
2047 if (result == pkgPackageManager::Failed) {
2052 if (result != pkgPackageManager::Completed) {
2057 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2059 _assert(list.ReadMainList());
2060 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2061 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2064 if (![before isEqualToArray:after])
2069 _assert(pkgDistUpgrade(cache_));
2073 [self updateWithStatus:status_];
2076 - (void) updateWithStatus:(Status &)status {
2078 _assert(list.ReadMainList());
2081 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2082 _assert(!_error->PendingError());
2084 pkgAcquire fetcher(&status);
2085 _assert(list.GetIndexes(&fetcher));
2087 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2088 bool failed = false;
2089 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2090 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2091 (*item)->Finished();
2095 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2096 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2097 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2100 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2105 - (void) setDelegate:(id)delegate {
2106 delegate_ = delegate;
2107 status_.setDelegate(delegate);
2108 progress_.setDelegate(delegate);
2111 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2112 pkgIndexFile *index(NULL);
2113 list_->FindIndex(file, index);
2114 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2120 /* Confirmation View {{{ */
2121 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2122 if ([packages count] == 0)
2125 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2126 [fields setObject:text forKey:key];
2128 CGColor blue(space_, 0, 0, 0.4, 1);
2129 [text setTextColor:[UIColor colorWithCGColor:blue]];
2132 @protocol ConfirmationViewDelegate
2137 @interface ConfirmationView : UIView {
2138 Database *database_;
2140 UITransitionView *transition_;
2142 UINavigationBar *navbar_;
2143 UIPreferencesTable *table_;
2144 NSMutableDictionary *fields_;
2145 UIActionSheet *essential_;
2150 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2154 @implementation ConfirmationView
2157 [navbar_ setDelegate:nil];
2158 [transition_ setDelegate:nil];
2159 [table_ setDataSource:nil];
2161 [transition_ release];
2166 if (essential_ != nil)
2167 [essential_ release];
2172 [transition_ transition:7 toView:nil];
2176 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2177 if (from != nil && to == nil)
2178 [self removeFromSuperview];
2181 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2184 if (essential_ != nil)
2185 [essential_ popupAlertAnimated:YES];
2187 [delegate_ confirm];
2196 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2197 NSString *context = [sheet context];
2199 if ([context isEqualToString:@"remove"])
2205 [delegate_ confirm];
2210 else if ([context isEqualToString:@"unable"])
2216 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2220 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2222 case 0: return @"Statistics";
2223 case 1: return @"Modifications";
2225 default: _assert(false);
2229 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2232 case 1: return [fields_ count];
2234 default: _assert(false);
2238 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2239 if (group != 1 || row == -1)
2242 _assert(size_t(row) < [fields_ count]);
2243 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2247 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2248 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2249 [cell setShowSelection:NO];
2252 case 0: switch (row) {
2254 [cell setTitle:@"Downloading"];
2255 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2259 [cell setTitle:@"Resuming At"];
2260 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2264 double size([database_ cache]->UsrSize());
2267 [cell setTitle:@"Disk Freeing"];
2268 [cell setValue:SizeString(-size)];
2270 [cell setTitle:@"Disk Using"];
2271 [cell setValue:SizeString(size)];
2275 default: _assert(false);
2279 _assert(size_t(row) < [fields_ count]);
2280 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2281 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2284 default: _assert(false);
2290 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2291 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2292 database_ = database;
2293 delegate_ = delegate;
2295 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2296 [self addSubview:transition_];
2298 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2300 CGSize navsize = [UINavigationBar defaultSize];
2301 CGRect navrect = {{0, 0}, navsize};
2302 CGRect bounds = [overlay_ bounds];
2304 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2305 [navbar_ setDelegate:self];
2307 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2308 [navbar_ pushNavigationItem:navitem];
2309 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2311 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2313 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2314 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2315 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2316 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2317 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2321 pkgCacheFile &cache([database_ cache]);
2322 NSArray *packages = [database_ packages];
2323 for (size_t i(0), e = [packages count]; i != e; ++i) {
2324 Package *package = [packages objectAtIndex:i];
2325 pkgCache::PkgIterator iterator = [package iterator];
2326 pkgDepCache::StateCache &state(cache[iterator]);
2328 NSString *name([package name]);
2330 if (state.NewInstall())
2331 [installing addObject:name];
2332 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2333 [reinstalling addObject:name];
2334 else if (state.Upgrade())
2335 [upgrading addObject:name];
2336 else if (state.Downgrade())
2337 [downgrading addObject:name];
2338 else if (state.Delete()) {
2339 if ([package essential])
2341 [removing addObject:name];
2347 else if (Advanced_ || true) {
2348 essential_ = [[UIActionSheet alloc]
2349 initWithTitle:@"Removing Essentials"
2350 buttons:[NSArray arrayWithObjects:
2351 @"Cancel Operation (Safe)",
2352 @"Force Removal (Unsafe)",
2354 defaultButtonIndex:0
2360 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2362 [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."];
2364 essential_ = [[UIActionSheet alloc]
2365 initWithTitle:@"Unable to Comply"
2366 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2367 defaultButtonIndex:0
2372 [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."];
2375 AddTextView(fields_, installing, @"Installing");
2376 AddTextView(fields_, reinstalling, @"Reinstalling");
2377 AddTextView(fields_, upgrading, @"Upgrading");
2378 AddTextView(fields_, downgrading, @"Downgrading");
2379 AddTextView(fields_, removing, @"Removing");
2381 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2382 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2385 [table_ setReusesTableCells:YES];
2386 [table_ setDataSource:self];
2387 [table_ reloadData];
2389 [overlay_ addSubview:navbar_];
2390 [overlay_ addSubview:table_];
2392 [view addSubview:self];
2394 [transition_ setDelegate:self];
2396 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2397 [transition_ transition:0 toView:blank];
2398 [transition_ transition:3 toView:overlay_];
2405 /* Progress Data {{{ */
2406 @interface ProgressData : NSObject {
2412 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2419 @implementation ProgressData
2421 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2422 if ((self = [super init]) != nil) {
2423 selector_ = selector;
2443 /* Progress View {{{ */
2444 @interface ProgressView : UIView <
2445 ConfigurationDelegate,
2448 _transient Database *database_;
2450 UIView *background_;
2451 UITransitionView *transition_;
2453 UINavigationBar *navbar_;
2454 UIProgressBar *progress_;
2455 UITextView *output_;
2456 UITextLabel *status_;
2457 UIPushButton *close_;
2460 SHA1SumValue springlist_;
2463 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2465 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2466 - (void) setContentView:(UIView *)view;
2469 - (void) _retachThread;
2470 - (void) _detachNewThreadData:(ProgressData *)data;
2471 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2477 @protocol ProgressViewDelegate
2478 - (void) progressViewIsComplete:(ProgressView *)sender;
2481 @implementation ProgressView
2484 [transition_ setDelegate:nil];
2485 [navbar_ setDelegate:nil];
2488 if (background_ != nil)
2489 [background_ release];
2490 [transition_ release];
2493 [progress_ release];
2500 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2501 if (bootstrap_ && from == overlay_ && to == view_)
2505 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2506 if ((self = [super initWithFrame:frame]) != nil) {
2507 database_ = database;
2508 delegate_ = delegate;
2510 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2511 [transition_ setDelegate:self];
2513 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2516 [overlay_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2518 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2519 [background_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2520 [self addSubview:background_];
2523 [self addSubview:transition_];
2525 CGSize navsize = [UINavigationBar defaultSize];
2526 CGRect navrect = {{0, 0}, navsize};
2528 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2529 [overlay_ addSubview:navbar_];
2531 [navbar_ setBarStyle:1];
2532 [navbar_ setDelegate:self];
2534 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2535 [navbar_ pushNavigationItem:navitem];
2537 CGRect bounds = [overlay_ bounds];
2538 CGSize prgsize = [UIProgressBar defaultSize];
2541 (bounds.size.width - prgsize.width) / 2,
2542 bounds.size.height - prgsize.height - 20
2545 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2546 [progress_ setStyle:0];
2548 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2550 bounds.size.height - prgsize.height - 50,
2551 bounds.size.width - 20,
2555 [status_ setColor:[UIColor colorWithCGColor:White_]];
2556 [status_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2558 [status_ setCentersHorizontally:YES];
2559 //[status_ setFont:font];
2561 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2563 navrect.size.height + 20,
2564 bounds.size.width - 20,
2565 bounds.size.height - navsize.height - 62 - navrect.size.height
2568 //[output_ setTextFont:@"Courier New"];
2569 [output_ setTextSize:12];
2571 [output_ setTextColor:[UIColor colorWithCGColor:White_]];
2572 [output_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2574 [output_ setMarginTop:0];
2575 [output_ setAllowsRubberBanding:YES];
2576 [output_ setEditable:NO];
2578 [overlay_ addSubview:output_];
2580 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2582 bounds.size.height - prgsize.height - 50,
2583 bounds.size.width - 20,
2587 [close_ setAutosizesToFit:NO];
2588 [close_ setDrawsShadow:YES];
2589 [close_ setStretchBackground:YES];
2590 [close_ setEnabled:YES];
2592 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2593 [close_ setTitleFont:bold];
2595 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2596 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2597 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2601 - (void) setContentView:(UIView *)view {
2602 view_ = [view retain];
2605 - (void) resetView {
2606 [transition_ transition:6 toView:view_];
2609 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2610 NSString *context = [sheet context];
2611 if ([context isEqualToString:@"conffile"]) {
2612 FILE *input = [database_ input];
2616 fprintf(input, "N\n");
2620 fprintf(input, "Y\n");
2631 - (void) closeButtonPushed {
2634 [delegate_ progressViewIsComplete:self];
2639 [delegate_ suspendWithAnimation:YES];
2643 system("killall SpringBoard");
2647 system("launchctl unload /System/Library/LaunchDaemons/com.apple.SpringBoard.plist; launchctl load /System/Library/LaunchDaemons/com.apple.SpringBoard.plist");
2656 - (void) _retachThread {
2657 UINavigationItem *item = [navbar_ topItem];
2658 [item setTitle:@"Complete"];
2660 [overlay_ addSubview:close_];
2661 [progress_ removeFromSuperview];
2662 [status_ removeFromSuperview];
2665 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2666 MMap mmap(file, MMap::ReadOnly);
2668 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2669 if (!(springlist_ == sha1.Result()))
2674 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2675 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2676 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2677 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2678 case 4: [close_ setTitle:@"Reboot Device"]; break;
2682 notify_post("com.apple.mobile.application_installed");
2685 [delegate_ setStatusBarShowsProgress:NO];
2690 - (void) _detachNewThreadData:(ProgressData *)data {
2691 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2693 [[data target] performSelector:[data selector] withObject:[data object]];
2696 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2701 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2702 UINavigationItem *item = [navbar_ topItem];
2703 [item setTitle:title];
2705 [status_ setText:nil];
2706 [output_ setText:@""];
2707 [progress_ setProgress:0];
2709 [close_ removeFromSuperview];
2710 [overlay_ addSubview:progress_];
2711 [overlay_ addSubview:status_];
2713 [delegate_ setStatusBarShowsProgress:YES];
2717 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2718 MMap mmap(file, MMap::ReadOnly);
2720 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2721 springlist_ = sha1.Result();
2724 [transition_ transition:6 toView:overlay_];
2727 detachNewThreadSelector:@selector(_detachNewThreadData:)
2729 withObject:[[ProgressData alloc]
2730 initWithSelector:selector
2737 - (void) repairWithSelector:(SEL)selector {
2739 detachNewThreadSelector:selector
2742 title:@"Repairing..."
2746 - (void) setConfigurationData:(NSString *)data {
2748 performSelectorOnMainThread:@selector(_setConfigurationData:)
2754 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2755 Package *package = id == nil ? nil : [database_ packageWithName:id];
2757 UIActionSheet *sheet = [[[UIActionSheet alloc]
2758 initWithTitle:(package == nil ? @"Source Error" : [package name])
2759 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2760 defaultButtonIndex:0
2765 [sheet setBodyText:error];
2766 [sheet popupAlertAnimated:YES];
2769 - (void) setProgressTitle:(NSString *)title {
2771 performSelectorOnMainThread:@selector(_setProgressTitle:)
2777 - (void) setProgressPercent:(float)percent {
2779 performSelectorOnMainThread:@selector(_setProgressPercent:)
2780 withObject:[NSNumber numberWithFloat:percent]
2785 - (void) addProgressOutput:(NSString *)output {
2787 performSelectorOnMainThread:@selector(_addProgressOutput:)
2793 - (void) _setConfigurationData:(NSString *)data {
2794 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2796 _assert(conffile_r(data));
2798 NSString *ofile = conffile_r[1];
2799 //NSString *nfile = conffile_r[2];
2801 UIActionSheet *sheet = [[[UIActionSheet alloc]
2802 initWithTitle:@"Configuration Upgrade"
2803 buttons:[NSArray arrayWithObjects:
2804 @"Keep My Old Copy",
2805 @"Accept The New Copy",
2806 // XXX: @"See What Changed",
2808 defaultButtonIndex:0
2813 [sheet setBodyText:[NSString stringWithFormat:
2814 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2817 [sheet popupAlertAnimated:YES];
2820 - (void) _setProgressTitle:(NSString *)title {
2821 [status_ setText:[title stringByAppendingString:@"..."]];
2824 - (void) _setProgressPercent:(NSNumber *)percent {
2825 [progress_ setProgress:[percent floatValue]];
2828 - (void) _addProgressOutput:(NSString *)output {
2829 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2830 CGSize size = [output_ contentSize];
2831 CGRect rect = {{0, size.height}, {size.width, 0}};
2832 [output_ scrollRectToVisible:rect animated:YES];
2835 - (BOOL) isRunning {
2842 /* Package Cell {{{ */
2843 @interface PackageCell : UISimpleTableCell {
2846 NSString *description_;
2848 //UIImageView *trusted_;
2850 UIImageView *badge_;
2851 UITextLabel *status_;
2855 - (PackageCell *) init;
2856 - (void) setPackage:(Package *)package;
2858 + (int) heightForPackage:(Package *)package;
2862 @implementation PackageCell
2864 - (void) clearPackage {
2875 if (description_ != nil) {
2876 [description_ release];
2880 if (source_ != nil) {
2887 [self clearPackage];
2892 //[trusted_ release];
2896 - (PackageCell *) init {
2897 if ((self = [super init]) != nil) {
2899 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2901 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2902 [status_ setBackgroundColor:Clear_];
2903 [status_ setFont:small];
2908 - (void) setPackage:(Package *)package {
2909 [self clearPackage];
2911 Source *source = [package source];
2914 if (NSString *icon = [package icon])
2915 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2916 if (icon_ == nil) if (NSString *section = [package section])
2917 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sections/%@.png", Simplify(section)]];
2918 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2919 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2921 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2923 icon_ = [icon_ retain];
2925 name_ = [[package name] retain];
2926 description_ = [[package tagline] retain];
2928 NSString *label = nil;
2929 bool trusted = false;
2931 if (source != nil) {
2932 label = [source label];
2933 trusted = [source trusted];
2934 } else if ([[package id] isEqualToString:@"firmware"])
2937 label = @"Unknown/Local";
2939 NSString *from = [NSString stringWithFormat:@"from %@", label];
2941 NSString *section = Simplify([package section]);
2942 if (section != nil && ![section isEqualToString:label])
2943 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2945 source_ = [from retain];
2948 [badge_ removeFromSuperview];
2949 [status_ removeFromSuperview];
2951 if (NSString *mode = [package mode]) {
2952 [badge_ setImage:[UIImage applicationImageNamed:
2953 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2956 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2957 [status_ setColor:Blueish_];
2958 } else if ([package half]) {
2959 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2960 [status_ setText:@"Package Damaged"];
2961 [status_ setColor:Red_];
2963 [badge_ setImage:nil];
2964 [status_ setText:nil];
2968 [self addSubview:badge_];
2969 [self addSubview:status_];
2974 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2976 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
2983 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
2984 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
2988 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
2990 [super drawContentInRect:rect selected:selected];
2993 + (int) heightForPackage:(Package *)package {
2994 NSString *tagline([package tagline]);
2995 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
2997 if ([package hasMode] || [package half])
3006 /* Section Cell {{{ */
3007 @interface SectionCell : UISimpleTableCell {
3012 _UISwitchSlider *switch_;
3017 - (void) setSection:(Section *)section editing:(BOOL)editing;
3021 @implementation SectionCell
3023 - (void) clearSection {
3024 if (section_ != nil) {
3034 if (count_ != nil) {
3041 [self clearSection];
3048 if ((self = [super init]) != nil) {
3049 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3051 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3052 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3056 - (void) onSwitch:(id)sender {
3057 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3058 if (metadata == nil) {
3059 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3060 [Sections_ setObject:metadata forKey:section_];
3064 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3067 - (void) setSection:(Section *)section editing:(BOOL)editing {
3068 if (editing != editing_) {
3070 [switch_ removeFromSuperview];
3072 [self addSubview:switch_];
3076 [self clearSection];
3078 if (section == nil) {
3079 name_ = [@"All Packages" retain];
3082 section_ = [section name];
3083 if (section_ != nil)
3084 section_ = [section_ retain];
3085 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3086 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3089 [switch_ setValue:isSectionVisible(section_) animated:NO];
3093 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3094 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3101 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3103 CGSize size = [count_ sizeWithFont:Font14_];
3107 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3109 [super drawContentInRect:rect selected:selected];
3115 /* File Table {{{ */
3116 @interface FileTable : RVPage {
3117 _transient Database *database_;
3120 NSMutableArray *files_;
3124 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3125 - (void) setPackage:(Package *)package;
3129 @implementation FileTable
3132 if (package_ != nil)
3141 - (int) numberOfRowsInTable:(UITable *)table {
3142 return files_ == nil ? 0 : [files_ count];
3145 - (float) table:(UITable *)table heightForRow:(int)row {
3149 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3150 if (reusing == nil) {
3151 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3152 UIFont *font = [UIFont systemFontOfSize:16];
3153 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3155 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3159 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3163 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3164 if ((self = [super initWithBook:book]) != nil) {
3165 database_ = database;
3167 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3169 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3170 [self addSubview:list_];
3172 UITableColumn *column = [[[UITableColumn alloc]
3173 initWithTitle:@"Name"
3175 width:[self frame].size.width
3178 [list_ setDataSource:self];
3179 [list_ setSeparatorStyle:1];
3180 [list_ addTableColumn:column];
3181 [list_ setDelegate:self];
3182 [list_ setReusesTableCells:YES];
3186 - (void) setPackage:(Package *)package {
3187 if (package_ != nil) {
3188 [package_ autorelease];
3197 [files_ removeAllObjects];
3199 if (package != nil) {
3200 package_ = [package retain];
3201 name_ = [[package id] retain];
3203 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3206 std::ifstream fin([path UTF8String]);
3208 while (std::getline(fin, line))
3209 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3212 if ([files_ count] != 0) {
3213 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3214 [files_ removeObjectAtIndex:0];
3215 [files_ sortUsingSelector:@selector(compareByPath:)];
3217 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3218 [stack addObject:@"/"];
3220 for (int i(0), e([files_ count]); i != e; ++i) {
3221 NSString *file = [files_ objectAtIndex:i];
3222 while (![file hasPrefix:[stack lastObject]])
3223 [stack removeLastObject];
3224 NSString *directory = [stack lastObject];
3225 [stack addObject:[file stringByAppendingString:@"/"]];
3226 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3227 ([stack count] - 2) * 3, "",
3228 [file substringFromIndex:[directory length]]
3237 - (void) resetViewAnimated:(BOOL)animated {
3238 [list_ resetViewAnimated:animated];
3241 - (void) reloadData {
3242 [self setPackage:[database_ packageWithName:name_]];
3243 [self reloadButtons];
3246 - (NSString *) title {
3247 return @"Installed Files";
3250 - (NSString *) backButtonTitle {
3256 /* Package View {{{ */
3257 @interface PackageView : BrowserView {
3260 NSMutableArray *buttons_;
3263 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3264 - (void) setPackage:(Package *)package;
3268 @implementation PackageView
3271 if (package_ != nil)
3279 - (void) _clickButtonWithName:(NSString *)name {
3280 if ([name isEqualToString:@"Install"])
3281 [delegate_ installPackage:package_];
3282 else if ([name isEqualToString:@"Reinstall"])
3283 [delegate_ installPackage:package_];
3284 else if ([name isEqualToString:@"Remove"])
3285 [delegate_ removePackage:package_];
3286 else if ([name isEqualToString:@"Upgrade"])
3287 [delegate_ installPackage:package_];
3288 else _assert(false);
3291 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3292 int count = [buttons_ count];
3293 _assert(count != 0);
3294 _assert(button <= count + 1);
3296 if (count != button - 1)
3297 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3302 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3303 [window setValue:package_ forKey:@"package"];
3306 - (void) _rightButtonClicked {
3307 /*[super _rightButtonClicked];
3310 int count = [buttons_ count];
3311 _assert(count != 0);
3314 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3316 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3317 [buttons addObjectsFromArray:buttons_];
3318 [buttons addObject:@"Cancel"];
3320 [delegate_ slideUp:[[[UIActionSheet alloc]
3323 defaultButtonIndex:2
3330 - (NSString *) _rightButtonTitle {
3331 int count = [buttons_ count];
3332 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3335 - (NSString *) title {
3339 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3340 if ((self = [super initWithBook:book database:database]) != nil) {
3341 database_ = database;
3342 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3346 - (void) setPackage:(Package *)package {
3347 if (package_ != nil) {
3348 [package_ autorelease];
3357 [buttons_ removeAllObjects];
3359 if (package != nil) {
3360 package_ = [package retain];
3361 name_ = [[package id] retain];
3363 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3365 if ([package_ source] == nil);
3366 else if ([package_ upgradableAndEssential:NO])
3367 [buttons_ addObject:@"Upgrade"];
3368 else if ([package_ installed] == nil)
3369 [buttons_ addObject:@"Install"];
3371 [buttons_ addObject:@"Reinstall"];
3372 if ([package_ installed] != nil)
3373 [buttons_ addObject:@"Remove"];
3377 - (void) reloadData {
3378 [self setPackage:[database_ packageWithName:name_]];
3379 [self reloadButtons];
3384 /* Package Table {{{ */
3385 @interface PackageTable : RVPage {
3386 _transient Database *database_;
3390 NSMutableArray *packages_;
3391 NSMutableArray *sections_;
3392 UISectionList *list_;
3395 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3397 - (void) setDelegate:(id)delegate;
3398 - (void) setObject:(id)object;
3400 - (void) reloadData;
3401 - (void) resetCursor;
3403 - (UISectionList *) list;
3405 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3409 @implementation PackageTable
3412 [list_ setDataSource:nil];
3417 [packages_ release];
3418 [sections_ release];
3423 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3424 return [sections_ count];
3427 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3428 return [[sections_ objectAtIndex:section] name];
3431 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3432 return [[sections_ objectAtIndex:section] row];
3435 - (int) numberOfRowsInTable:(UITable *)table {
3436 return [packages_ count];
3439 - (float) table:(UITable *)table heightForRow:(int)row {
3440 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3443 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3445 reusing = [[[PackageCell alloc] init] autorelease];
3446 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3450 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3454 - (void) tableRowSelected:(NSNotification *)notification {
3455 int row = [[notification object] selectedRow];
3459 Package *package = [packages_ objectAtIndex:row];
3460 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3461 [view setDelegate:delegate_];
3462 [view setPackage:package];
3463 [book_ pushPage:view];
3466 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3467 if ((self = [super initWithBook:book]) != nil) {
3468 database_ = database;
3469 title_ = [title retain];
3471 object_ = object == nil ? nil : [object retain];
3473 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3474 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3476 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3477 [list_ setDataSource:self];
3479 UITableColumn *column = [[[UITableColumn alloc]
3480 initWithTitle:@"Name"
3482 width:[self frame].size.width
3485 UITable *table = [list_ table];
3486 [table setSeparatorStyle:1];
3487 [table addTableColumn:column];
3488 [table setDelegate:self];
3489 [table setReusesTableCells:YES];
3491 [self addSubview:list_];
3496 - (void) setDelegate:(id)delegate {
3497 delegate_ = delegate;
3500 - (void) setObject:(id)object {
3506 object_ = [object retain];
3509 - (void) reloadData {
3510 NSArray *packages = [database_ packages];
3512 [packages_ removeAllObjects];
3513 [sections_ removeAllObjects];
3515 for (size_t i(0); i != [packages count]; ++i) {
3516 Package *package([packages objectAtIndex:i]);
3517 if ([[package performSelector:filter_ withObject:object_] boolValue])
3518 [packages_ addObject:package];
3521 Section *section = nil;
3523 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3524 Package *package = [packages_ objectAtIndex:offset];
3525 NSString *name = [package index];
3527 if (section == nil || ![[section name] isEqualToString:name]) {
3528 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3529 [sections_ addObject:section];
3532 [section addToCount];
3538 - (NSString *) title {
3542 - (void) resetViewAnimated:(BOOL)animated {
3543 [list_ resetViewAnimated:animated];
3546 - (void) resetCursor {
3547 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3550 - (UISectionList *) list {
3554 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3555 [list_ setShouldHideHeaderInShortLists:hide];
3561 /* Add Source View {{{ */
3562 @interface AddSourceView : RVPage {
3563 _transient Database *database_;
3566 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3570 @implementation AddSourceView
3572 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3573 if ((self = [super initWithBook:book]) != nil) {
3574 database_ = database;
3580 /* Source Cell {{{ */
3581 @interface SourceCell : UITableCell {
3584 NSString *description_;
3590 - (SourceCell *) initWithSource:(Source *)source;
3594 @implementation SourceCell
3599 [description_ release];
3604 - (SourceCell *) initWithSource:(Source *)source {
3605 if ((self = [super init]) != nil) {
3607 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3609 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3610 icon_ = [icon_ retain];
3612 origin_ = [[source name] retain];
3613 label_ = [[source uri] retain];
3614 description_ = [[source description] retain];
3618 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3620 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3627 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3631 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3635 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3637 [super drawContentInRect:rect selected:selected];
3642 /* Source Table {{{ */
3643 @interface SourceTable : RVPage {
3644 _transient Database *database_;
3645 UISectionList *list_;
3646 NSMutableArray *sources_;
3647 UIActionSheet *alert_;
3651 UIProgressHUD *hud_;
3654 //NSURLConnection *installer_;
3655 NSURLConnection *trivial_bz2_;
3656 NSURLConnection *trivial_gz_;
3657 //NSURLConnection *automatic_;
3662 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3666 @implementation SourceTable
3668 - (void) _deallocConnection:(NSURLConnection *)connection {
3669 if (connection != nil) {
3670 [connection cancel];
3671 //[connection setDelegate:nil];
3672 [connection release];
3677 [[list_ table] setDelegate:nil];
3678 [list_ setDataSource:nil];
3687 //[self _deallocConnection:installer_];
3688 [self _deallocConnection:trivial_gz_];
3689 [self _deallocConnection:trivial_bz2_];
3690 //[self _deallocConnection:automatic_];
3697 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3698 return offset_ == 0 ? 1 : 2;
3701 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3702 switch (section + (offset_ == 0 ? 1 : 0)) {
3703 case 0: return @"Entered by User";
3704 case 1: return @"Installed by Packages";
3712 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3713 switch (section + (offset_ == 0 ? 1 : 0)) {
3715 case 1: return offset_;
3723 - (int) numberOfRowsInTable:(UITable *)table {
3724 return [sources_ count];
3727 - (float) table:(UITable *)table heightForRow:(int)row {
3728 Source *source = [sources_ objectAtIndex:row];
3729 return [source description] == nil ? 56 : 73;
3732 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3733 Source *source = [sources_ objectAtIndex:row];
3734 // XXX: weird warning, stupid selectors ;P
3735 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3738 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3742 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3746 - (void) tableRowSelected:(NSNotification*)notification {
3747 UITable *table([list_ table]);
3748 int row([table selectedRow]);
3752 Source *source = [sources_ objectAtIndex:row];
3754 PackageTable *packages = [[[PackageTable alloc]
3757 title:[source label]
3758 filter:@selector(isVisibleInSource:)
3762 [packages setDelegate:delegate_];
3764 [book_ pushPage:packages];
3767 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3768 Source *source = [sources_ objectAtIndex:row];
3769 return [source record] != nil;
3772 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3773 [[list_ table] setDeleteConfirmationRow:row];
3776 - (void) table:(UITable *)table deleteRow:(int)row {
3777 Source *source = [sources_ objectAtIndex:row];
3778 [Sources_ removeObjectForKey:[source key]];
3779 [delegate_ syncData];
3782 - (void) _endConnection:(NSURLConnection *)connection {
3783 NSURLConnection **field = NULL;
3784 if (connection == trivial_bz2_)
3785 field = &trivial_bz2_;
3786 else if (connection == trivial_gz_)
3787 field = &trivial_gz_;
3788 _assert(field != NULL);
3789 [connection release];
3793 trivial_bz2_ == nil &&
3796 [delegate_ setStatusBarShowsProgress:NO];
3799 [hud_ removeFromSuperview];
3804 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3807 @"./", @"Distribution",
3808 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3810 [delegate_ syncData];
3811 } else if (error_ != nil) {
3812 UIActionSheet *sheet = [[[UIActionSheet alloc]
3813 initWithTitle:@"Verification Error"
3814 buttons:[NSArray arrayWithObjects:@"OK", nil]
3815 defaultButtonIndex:0
3820 [sheet setBodyText:[error_ localizedDescription]];
3821 [sheet popupAlertAnimated:YES];
3823 UIActionSheet *sheet = [[[UIActionSheet alloc]
3824 initWithTitle:@"Did not Find Repository"
3825 buttons:[NSArray arrayWithObjects:@"OK", nil]
3826 defaultButtonIndex:0
3831 [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."];
3832 [sheet popupAlertAnimated:YES];
3838 if (error_ != nil) {
3845 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3846 switch ([response statusCode]) {
3852 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3853 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3855 error_ = [error retain];
3856 [self _endConnection:connection];
3859 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3860 [self _endConnection:connection];
3863 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
3864 NSMutableURLRequest *request = [NSMutableURLRequest
3865 requestWithURL:[NSURL URLWithString:href]
3866 cachePolicy:NSURLRequestUseProtocolCachePolicy
3867 timeoutInterval:20.0
3870 [request setHTTPMethod:method];
3872 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
3875 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3876 NSString *context = [sheet context];
3877 if ([context isEqualToString:@"source"])
3880 NSString *href = [[sheet textField] text];
3882 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
3884 if (![href hasSuffix:@"/"])
3885 href_ = [href stringByAppendingString:@"/"];
3888 href_ = [href_ retain];
3890 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
3891 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
3892 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
3896 hud_ = [delegate_ addProgressHUD];
3897 [hud_ setText:@"Verifying URL"];
3910 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3911 if ((self = [super initWithBook:book]) != nil) {
3912 database_ = database;
3913 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
3915 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
3916 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3917 [list_ setShouldHideHeaderInShortLists:NO];
3919 [self addSubview:list_];
3920 [list_ setDataSource:self];
3922 UITableColumn *column = [[UITableColumn alloc]
3923 initWithTitle:@"Name"
3925 width:[self frame].size.width
3928 UITable *table = [list_ table];
3929 [table setSeparatorStyle:1];
3930 [table addTableColumn:column];
3931 [table setDelegate:self];
3937 - (void) reloadData {
3939 _assert(list.ReadMainList());
3941 [sources_ removeAllObjects];
3942 [sources_ addObjectsFromArray:[database_ sources]];
3943 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
3945 int count = [sources_ count];
3946 for (offset_ = 0; offset_ != count; ++offset_) {
3947 Source *source = [sources_ objectAtIndex:offset_];
3948 if ([source record] == nil)
3955 - (void) resetViewAnimated:(BOOL)animated {
3956 [list_ resetViewAnimated:animated];
3959 - (void) _leftButtonClicked {
3960 /*[book_ pushPage:[[[AddSourceView alloc]
3965 UIActionSheet *sheet = [[[UIActionSheet alloc]
3966 initWithTitle:@"Enter Cydia/APT URL"
3967 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
3968 defaultButtonIndex:0
3973 [sheet addTextFieldWithValue:@"http://" label:@""];
3975 UITextInputTraits *traits = [[sheet textField] textInputTraits];
3976 [traits setAutocapitalizationType:0];
3977 [traits setKeyboardType:3];
3978 [traits setAutocorrectionType:1];
3980 [sheet popupAlertAnimated:YES];
3983 - (void) _rightButtonClicked {
3984 UITable *table = [list_ table];
3985 BOOL editing = [table isRowDeletionEnabled];
3986 [table enableRowDeletion:!editing animated:YES];
3987 [book_ reloadButtonsForPage:self];
3990 - (NSString *) title {
3994 - (NSString *) backButtonTitle {
3998 - (NSString *) leftButtonTitle {
3999 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4002 - (NSString *) rightButtonTitle {
4003 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4006 - (RVUINavBarButtonStyle) rightButtonStyle {
4007 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4013 /* Installed View {{{ */
4014 @interface InstalledView : RVPage {
4015 _transient Database *database_;
4016 PackageTable *packages_;
4020 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4024 @implementation InstalledView
4027 [packages_ release];
4031 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4032 if ((self = [super initWithBook:book]) != nil) {
4033 database_ = database;
4035 packages_ = [[PackageTable alloc]
4039 filter:@selector(isInstalledAndVisible:)
4040 with:[NSNumber numberWithBool:YES]
4043 [self addSubview:packages_];
4047 - (void) resetViewAnimated:(BOOL)animated {
4048 [packages_ resetViewAnimated:animated];
4051 - (void) reloadData {
4052 [packages_ reloadData];
4055 - (void) _rightButtonClicked {
4056 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4057 [packages_ reloadData];
4059 [book_ reloadButtonsForPage:self];
4062 - (NSString *) title {
4063 return @"Installed";
4066 - (NSString *) backButtonTitle {
4070 - (NSString *) rightButtonTitle {
4071 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4074 - (RVUINavBarButtonStyle) rightButtonStyle {
4075 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4078 - (void) setDelegate:(id)delegate {
4079 [super setDelegate:delegate];
4080 [packages_ setDelegate:delegate];
4087 @interface HomeView : BrowserView {
4092 @implementation HomeView
4094 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4098 - (void) _leftButtonClicked {
4099 UIActionSheet *sheet = [[[UIActionSheet alloc]
4100 initWithTitle:@"About Cydia Installer"
4101 buttons:[NSArray arrayWithObjects:@"Close", nil]
4102 defaultButtonIndex:0
4108 @"Copyright (C) 2008\n"
4109 "Jay Freeman (saurik)\n"
4110 "saurik@saurik.com\n"
4111 "http://www.saurik.com/\n"
4114 "http://www.theokorigroup.com/\n"
4116 "College of Creative Studies,\n"
4117 "University of California,\n"
4119 "http://www.ccs.ucsb.edu/"
4122 [sheet popupAlertAnimated:YES];
4125 - (NSString *) leftButtonTitle {
4131 /* Manage View {{{ */
4132 @interface ManageView : BrowserView {
4137 @implementation ManageView
4139 - (NSString *) title {
4143 - (void) _leftButtonClicked {
4144 [delegate_ askForSettings];
4147 - (NSString *) leftButtonTitle {
4151 - (NSString *) _rightButtonTitle {
4158 /* Browser Implementation {{{ */
4159 @implementation BrowserView
4162 WebView *webview = [webview_ webView];
4163 [webview setFrameLoadDelegate:nil];
4164 [webview setResourceLoadDelegate:nil];
4165 [webview setUIDelegate:nil];
4167 [scroller_ setDelegate:nil];
4168 [webview_ setDelegate:nil];
4170 [scroller_ release];
4173 [indicator_ release];
4179 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4180 [self loadRequest:[NSURLRequest
4183 timeoutInterval:30.0
4187 - (void) loadURL:(NSURL *)url {
4188 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4191 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4192 NSMutableURLRequest *copy = [request mutableCopy];
4194 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4195 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4196 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4199 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4204 - (void) loadRequest:(NSURLRequest *)request {
4206 [webview_ loadRequest:request];
4209 - (void) reloadURL {
4210 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4211 [urls_ removeLastObject];
4212 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4215 - (WebView *) webView {
4216 return [webview_ webView];
4219 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4220 [scroller_ setContentSize:frame.size];
4223 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4224 [self view:sender didSetFrame:frame];
4227 - (void) pushPage:(RVPage *)page {
4228 [self setBackButtonTitle:title_];
4229 [page setDelegate:delegate_];
4230 [book_ pushPage:page];
4233 - (void) getSpecial:(NSString *)href {
4236 if ([href hasPrefix:@"mailto:"])
4237 [delegate_ openURL:[NSURL URLWithString:href]];
4238 else if ([href isEqualToString:@"cydia://add-source"])
4239 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4240 else if ([href isEqualToString:@"cydia://sources"])
4241 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4242 else if ([href isEqualToString:@"cydia://packages"])
4243 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4244 else if ([href hasPrefix:@"cydia://files/"]) {
4245 NSString *name = [href substringFromIndex:14];
4247 if (Package *package = [database_ packageWithName:name]) {
4248 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
4249 [files setPackage:package];
4252 } else if ([href hasPrefix:@"apptapp://package/"]) {
4253 NSString *name = [href substringFromIndex:18];
4255 if (Package *package = [database_ packageWithName:name]) {
4256 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4257 [view setPackage:package];
4260 UIActionSheet *sheet = [[[UIActionSheet alloc]
4261 initWithTitle:@"Cannot Locate Package"
4262 buttons:[NSArray arrayWithObjects:@"Close", nil]
4263 defaultButtonIndex:0
4268 [sheet setBodyText:[NSString stringWithFormat:
4269 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4272 [sheet popupAlertAnimated:YES];
4277 [self pushPage:page];
4280 - (void) webView:(WebView *)sender willClickElement:(id)element {
4281 if ([[element localName] isEqualToString:@"img"])
4282 do if ((element = [element parentNode]) == nil)
4284 while (![[element localName] isEqualToString:@"a"]);
4285 if (![element respondsToSelector:@selector(href)])
4287 NSString *href = [element href];
4290 [self getSpecial:href];
4293 - (BOOL) isSpecialScheme:(NSString *)scheme {
4295 [scheme isEqualToString:@"apptapp"] ||
4296 [scheme isEqualToString:@"cydia"] ||
4297 [scheme isEqualToString:@"mailto"];
4300 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4301 NSURL *url = [request URL];
4302 if ([self isSpecialScheme:[url scheme]]) {
4303 [self getSpecial:[url absoluteString]];
4309 [book_ pushPage:self];
4312 return [self _addHeadersToRequest:request];
4315 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4316 if (request != nil) {
4317 NSString *scheme = [[request URL] scheme];
4318 if ([self isSpecialScheme:scheme])
4322 [self setBackButtonTitle:title_];
4324 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4325 [browser setDelegate:delegate_];
4327 if (request != nil) {
4328 [browser loadRequest:[self _addHeadersToRequest:request]];
4329 [book_ pushPage:browser];
4332 return [browser webView];
4335 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4336 if ([frame parentFrame] != nil)
4339 title_ = [title retain];
4340 [self setTitle:title];
4343 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4344 if ([frame parentFrame] != nil)
4349 [indicator_ startAnimation];
4350 [self reloadButtons];
4352 if (title_ != nil) {
4357 [self setTitle:@"Loading..."];
4359 WebView *webview = [webview_ webView];
4360 NSString *href = [webview mainFrameURL];
4361 [urls_ addObject:[NSURL URLWithString:href]];
4363 CGRect webrect = [scroller_ frame];
4364 webrect.size.height = 0;
4365 [webview_ setFrame:webrect];
4368 - (void) _finishLoading {
4371 [indicator_ stopAnimation];
4372 [self reloadButtons];
4376 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4377 if ([frame parentFrame] != nil)
4379 [self _finishLoading];
4382 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4383 if ([frame parentFrame] != nil)
4385 [self _finishLoading];
4387 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4388 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4389 [[error localizedDescription] stringByAddingPercentEscapes]
4393 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4394 if ((self = [super initWithBook:book]) != nil) {
4395 database_ = database;
4398 struct CGRect bounds = [self bounds];
4400 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4401 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4402 [self addSubview:pinstripe];
4404 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4405 [self addSubview:scroller_];
4407 [scroller_ setScrollingEnabled:YES];
4408 [scroller_ setAdjustForContentSizeChange:YES];
4409 [scroller_ setClipsSubviews:YES];
4410 [scroller_ setAllowsRubberBanding:YES];
4411 [scroller_ setScrollDecelerationFactor:0.99];
4412 [scroller_ setDelegate:self];
4414 CGRect webrect = [scroller_ bounds];
4415 webrect.size.height = 0;
4417 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4418 [scroller_ addSubview:webview_];
4420 [webview_ setTilingEnabled:YES];
4421 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4422 [webview_ setAutoresizes:YES];
4423 [webview_ setDelegate:self];
4424 //[webview_ setEnabledGestures:2];
4426 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4427 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4428 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4430 Package *package([database_ packageWithName:@"cydia"]);
4431 NSString *application = package == nil ? @"Cydia" : [NSString
4432 stringWithFormat:@"Cydia/%@",
4436 WebView *webview = [webview_ webView];
4437 [webview setApplicationNameForUserAgent:application];
4438 [webview setFrameLoadDelegate:self];
4439 [webview setResourceLoadDelegate:self];
4440 [webview setUIDelegate:self];
4442 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4446 - (void) _rightButtonClicked {
4451 - (NSString *) _rightButtonTitle {
4455 - (NSString *) rightButtonTitle {
4456 return loading_ ? @"" : [self _rightButtonTitle];
4459 - (NSString *) title {
4463 - (NSString *) backButtonTitle {
4467 - (void) setPageActive:(BOOL)active {
4469 [book_ addSubview:indicator_];
4471 [indicator_ removeFromSuperview];
4474 - (void) resetViewAnimated:(BOOL)animated {
4477 - (void) setPushed:(bool)pushed {
4484 @interface CYBook : RVBook <
4487 _transient Database *database_;
4489 UIProgressIndicator *indicator_;
4490 UITextLabel *prompt_;
4491 UIProgressBar *progress_;
4495 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4501 /* Install View {{{ */
4502 @interface InstallView : RVPage {
4503 _transient Database *database_;
4504 NSMutableArray *sections_;
4505 NSMutableArray *filtered_;
4506 UITransitionView *transition_;
4512 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4513 - (void) reloadData;
4518 @implementation InstallView
4521 [list_ setDataSource:nil];
4522 [list_ setDelegate:nil];
4524 [sections_ release];
4525 [filtered_ release];
4526 [transition_ release];
4528 [accessory_ release];
4532 - (int) numberOfRowsInTable:(UITable *)table {
4533 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4536 - (float) table:(UITable *)table heightForRow:(int)row {
4540 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4542 reusing = [[[SectionCell alloc] init] autorelease];
4543 [(SectionCell *)reusing setSection:(editing_ ?
4544 [sections_ objectAtIndex:row] :
4545 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4546 ) editing:editing_];
4550 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4554 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4558 - (void) tableRowSelected:(NSNotification *)notification {
4559 int row = [[notification object] selectedRow];
4570 title = @"All Packages";
4572 section = [filtered_ objectAtIndex:(row - 1)];
4573 name = [section name];
4579 title = @"(No Section)";
4583 PackageTable *table = [[[PackageTable alloc]
4587 filter:@selector(isVisiblyUninstalledInSection:)
4591 [table setDelegate:delegate_];
4593 [book_ pushPage:table];
4596 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4597 if ((self = [super initWithBook:book]) != nil) {
4598 database_ = database;
4600 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4601 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4603 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4604 [self addSubview:transition_];
4606 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4607 [transition_ transition:0 toView:list_];
4609 UITableColumn *column = [[[UITableColumn alloc]
4610 initWithTitle:@"Name"
4612 width:[self frame].size.width
4615 [list_ setDataSource:self];
4616 [list_ setSeparatorStyle:1];
4617 [list_ addTableColumn:column];
4618 [list_ setDelegate:self];
4619 [list_ setReusesTableCells:YES];
4625 - (void) reloadData {
4626 NSArray *packages = [database_ packages];
4628 [sections_ removeAllObjects];
4629 [filtered_ removeAllObjects];
4631 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4632 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4634 for (size_t i(0); i != [packages count]; ++i) {
4635 Package *package([packages objectAtIndex:i]);
4636 NSString *name([package section]);
4639 Section *section([sections objectForKey:name]);
4640 if (section == nil) {
4641 section = [[[Section alloc] initWithName:name] autorelease];
4642 [sections setObject:section forKey:name];
4646 if ([package valid] && [package installed] == nil && [package visible])
4647 [filtered addObject:package];
4650 [sections_ addObjectsFromArray:[sections allValues]];
4651 [sections_ sortUsingSelector:@selector(compareByName:)];
4653 [filtered sortUsingSelector:@selector(compareBySection:)];
4655 Section *section = nil;
4656 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4657 Package *package = [filtered objectAtIndex:offset];
4658 NSString *name = [package section];
4660 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4661 section = name == nil ?
4662 [[[Section alloc] initWithName:nil] autorelease] :
4663 [sections objectForKey:name];
4664 [filtered_ addObject:section];
4667 [section addToCount];
4673 - (void) resetView {
4675 [self _rightButtonClicked];
4678 - (void) resetViewAnimated:(BOOL)animated {
4679 [list_ resetViewAnimated:animated];
4682 - (void) _rightButtonClicked {
4683 if ((editing_ = !editing_))
4686 [delegate_ updateData];
4689 [book_ setTitle:[self title] forPage:self];
4690 [book_ reloadButtonsForPage:self];
4693 - (NSString *) title {
4694 return editing_ ? @"Section Visibility" : @"Install by Section";
4697 - (NSString *) backButtonTitle {
4701 - (NSString *) rightButtonTitle {
4702 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4705 - (UIView *) accessoryView {
4711 /* Changes View {{{ */
4712 @interface ChangesView : RVPage {
4713 _transient Database *database_;
4714 NSMutableArray *packages_;
4715 NSMutableArray *sections_;
4716 UISectionList *list_;
4720 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4721 - (void) reloadData;
4725 @implementation ChangesView
4728 [[list_ table] setDelegate:nil];
4729 [list_ setDataSource:nil];
4731 [packages_ release];
4732 [sections_ release];
4737 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4738 return [sections_ count];
4741 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4742 return [[sections_ objectAtIndex:section] name];
4745 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4746 return [[sections_ objectAtIndex:section] row];
4749 - (int) numberOfRowsInTable:(UITable *)table {
4750 return [packages_ count];
4753 - (float) table:(UITable *)table heightForRow:(int)row {
4754 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4757 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4759 reusing = [[[PackageCell alloc] init] autorelease];
4760 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4764 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4768 - (void) tableRowSelected:(NSNotification *)notification {
4769 int row = [[notification object] selectedRow];
4772 Package *package = [packages_ objectAtIndex:row];
4773 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4774 [view setDelegate:delegate_];
4775 [view setPackage:package];
4776 [book_ pushPage:view];
4779 - (void) _leftButtonClicked {
4780 [(CYBook *)book_ update];
4781 [self reloadButtons];
4784 - (void) _rightButtonClicked {
4785 [delegate_ distUpgrade];
4788 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4789 if ((self = [super initWithBook:book]) != nil) {
4790 database_ = database;
4792 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4793 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4795 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4796 [self addSubview:list_];
4798 [list_ setShouldHideHeaderInShortLists:NO];
4799 [list_ setDataSource:self];
4800 //[list_ setSectionListStyle:1];
4802 UITableColumn *column = [[[UITableColumn alloc]
4803 initWithTitle:@"Name"
4805 width:[self frame].size.width
4808 UITable *table = [list_ table];
4809 [table setSeparatorStyle:1];
4810 [table addTableColumn:column];
4811 [table setDelegate:self];
4812 [table setReusesTableCells:YES];
4818 - (void) reloadData {
4819 NSArray *packages = [database_ packages];
4821 [packages_ removeAllObjects];
4822 [sections_ removeAllObjects];
4824 for (size_t i(0); i != [packages count]; ++i) {
4825 Package *package([packages objectAtIndex:i]);
4828 [package installed] == nil && [package valid] && [package visible] ||
4829 [package upgradableAndEssential:NO]
4831 [packages_ addObject:package];
4834 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4836 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4837 Section *section = nil;
4840 bool unseens = false;
4842 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4844 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4845 Package *package = [packages_ objectAtIndex:offset];
4847 if ([package upgradableAndEssential:YES]) {
4849 [upgradable addToCount];
4852 NSDate *seen = [package seen];
4857 name = [@"n/a ?" retain];
4859 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4862 if (section == nil || ![[section name] isEqualToString:name]) {
4863 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4864 [sections_ addObject:section];
4868 [section addToCount];
4872 CFRelease(formatter);
4875 Section *last = [sections_ lastObject];
4876 size_t count = [last count];
4877 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4878 [sections_ removeLastObject];
4882 [sections_ insertObject:upgradable atIndex:0];
4885 [self reloadButtons];
4888 - (void) resetViewAnimated:(BOOL)animated {
4889 [list_ resetViewAnimated:animated];
4892 - (NSString *) leftButtonTitle {
4893 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4896 - (NSString *) rightButtonTitle {
4897 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
4900 - (NSString *) title {
4906 /* Search View {{{ */
4907 @protocol SearchViewDelegate
4908 - (void) showKeyboard:(BOOL)show;
4911 @interface SearchView : RVPage {
4913 UISearchField *field_;
4914 UITransitionView *transition_;
4915 PackageTable *table_;
4916 UIPreferencesTable *advanced_;
4922 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4923 - (void) reloadData;
4927 @implementation SearchView
4931 [[field_ textTraits] setEditingDelegate:nil];
4933 [field_ setDelegate:nil];
4935 [accessory_ release];
4937 [transition_ release];
4939 [advanced_ release];
4944 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4948 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4950 case 0: return @"Advanced Search (Coming Soon!)";
4952 default: _assert(false);
4956 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4960 default: _assert(false);
4964 - (void) _showKeyboard:(BOOL)show {
4965 CGSize keysize = [UIKeyboard defaultSize];
4966 CGRect keydown = [book_ pageBounds];
4967 CGRect keyup = keydown;
4968 keyup.size.height -= keysize.height - ButtonBarHeight_;
4970 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4972 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4973 [animation setSignificantRectFields:8];
4976 [animation setStartFrame:keydown];
4977 [animation setEndFrame:keyup];
4979 [animation setStartFrame:keyup];
4980 [animation setEndFrame:keydown];
4983 UIAnimator *animator = [UIAnimator sharedAnimator];
4986 addAnimations:[NSArray arrayWithObjects:animation, nil]
4987 withDuration:(KeyboardTime_ - delay)
4992 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4995 [delegate_ showKeyboard:show];
4999 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5000 [self _showKeyboard:YES];
5003 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5004 [self _showKeyboard:NO];
5007 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5009 NSString *text([field_ text]);
5010 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5016 - (void) textFieldClearButtonPressed:(UITextField *)field {
5020 - (void) keyboardInputShouldDelete:(id)input {
5024 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5025 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5029 [field_ resignFirstResponder];
5034 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5035 if ((self = [super initWithBook:book]) != nil) {
5036 CGRect pageBounds = [book_ pageBounds];
5038 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5039 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5040 [self addSubview:pinstripe];*/
5042 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5043 [self addSubview:transition_];
5045 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5047 [advanced_ setReusesTableCells:YES];
5048 [advanced_ setDataSource:self];
5049 [advanced_ reloadData];
5051 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5052 CGColor dimmed(space_, 0, 0, 0, 0.5);
5053 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5055 table_ = [[PackageTable alloc]
5059 filter:@selector(isVisiblySearchedForBy:)
5063 [table_ setShouldHideHeaderInShortLists:NO];
5064 [transition_ transition:0 toView:table_];
5073 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5080 [self bounds].size.width - area.origin.x - 18;
5082 area.size.height = [UISearchField defaultHeight];
5084 field_ = [[UISearchField alloc] initWithFrame:area];
5086 UIFont *font = [UIFont systemFontOfSize:16];
5087 [field_ setFont:font];
5089 [field_ setPlaceholder:@"Package Names & Descriptions"];
5090 [field_ setDelegate:self];
5092 [field_ setPaddingTop:3];
5094 UITextInputTraits *traits = [field_ textInputTraits];
5095 [traits setAutocapitalizationType:0];
5096 [traits setAutocorrectionType:1];
5097 [traits setReturnKeyType:6];
5099 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5101 accessory_ = [[UIView alloc] initWithFrame:accrect];
5102 [accessory_ addSubview:field_];
5104 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5105 [configure setShowPressFeedback:YES];
5106 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5107 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5108 [accessory_ addSubview:configure];*/
5114 LKAnimation *animation = [LKTransition animation];
5115 [animation setType:@"oglFlip"];
5116 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5117 [animation setFillMode:@"extended"];
5118 [animation setTransitionFlags:3];
5119 [animation setDuration:10];
5120 [animation setSpeed:0.35];
5121 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5122 [[transition_ _layer] addAnimation:animation forKey:0];
5123 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5124 flipped_ = !flipped_;
5128 - (void) configurePushed {
5129 [field_ resignFirstResponder];
5133 - (void) resetViewAnimated:(BOOL)animated {
5136 [table_ resetViewAnimated:animated];
5139 - (void) reloadData {
5142 [table_ setObject:[field_ text]];
5143 [table_ reloadData];
5144 [table_ resetCursor];
5147 - (UIView *) accessoryView {
5151 - (NSString *) title {
5155 - (NSString *) backButtonTitle {
5159 - (void) setDelegate:(id)delegate {
5160 [table_ setDelegate:delegate];
5161 [super setDelegate:delegate];
5167 @implementation CYBook
5171 [indicator_ release];
5173 [progress_ release];
5177 - (NSString *) getTitleForPage:(RVPage *)page {
5178 return Simplify([super getTitleForPage:page]);
5186 [navbar_ setPrompt:@""];
5187 [navbar_ addSubview:overlay_];
5188 [indicator_ startAnimation];
5189 [prompt_ setText:@"Updating Database..."];
5190 [progress_ setProgress:0];
5195 detachNewThreadSelector:@selector(_update)
5204 [overlay_ removeFromSuperview];
5205 [indicator_ stopAnimation];
5206 [delegate_ reloadData];
5208 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5211 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5212 if ((self = [super initWithFrame:frame]) != nil) {
5213 database_ = database;
5215 CGRect ovrrect = [navbar_ bounds];
5216 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5218 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5220 bool ugly = [navbar_ _barStyle:NO] == 0;
5222 UIProgressIndicatorStyle style = ugly ?
5223 kUIProgressIndicatorStyleSmallBlack :
5224 kUIProgressIndicatorStyleSmallWhite;
5226 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5227 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5228 CGRect indrect = {{indoffset, indoffset}, indsize};
5230 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5231 [indicator_ setStyle:style];
5232 [overlay_ addSubview:indicator_];
5234 CGSize prmsize = {200, indsize.width + 4};
5237 indoffset * 2 + indsize.width,
5241 (ovrrect.size.height - prmsize.height) / 2
5244 UIFont *font = [UIFont systemFontOfSize:12];
5246 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5248 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : White_)]];
5249 [prompt_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
5250 [prompt_ setFont:font];
5252 [overlay_ addSubview:prompt_];
5254 CGSize prgsize = {75, 100};
5257 ovrrect.size.width - prgsize.width - 10,
5258 (ovrrect.size.height - prgsize.height) / 2
5261 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5262 [progress_ setStyle:0];
5263 [overlay_ addSubview:progress_];
5268 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5271 status.setDelegate(self);
5273 [database_ updateWithStatus:status];
5276 performSelectorOnMainThread:@selector(_update_)
5284 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5285 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5288 - (void) setProgressTitle:(NSString *)title {
5290 performSelectorOnMainThread:@selector(_setProgressTitle:)
5296 - (void) setProgressPercent:(float)percent {
5299 - (void) addProgressOutput:(NSString *)output {
5301 performSelectorOnMainThread:@selector(_addProgressOutput:)
5307 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5311 - (void) _setProgressTitle:(NSString *)title {
5312 [prompt_ setText:[title stringByAppendingString:@"..."]];
5315 - (void) _addProgressOutput:(NSString *)output {
5320 @interface Cydia : UIApplication <
5321 ConfirmationViewDelegate,
5322 ProgressViewDelegate,
5331 UIToolbar *buttonbar_;
5333 ConfirmationView *confirm_;
5335 NSMutableArray *essential_;
5336 NSMutableArray *broken_;
5338 Database *database_;
5339 ProgressView *progress_;
5343 UIKeyboard *keyboard_;
5344 UIProgressHUD *hud_;
5346 InstallView *install_;
5347 ChangesView *changes_;
5348 ManageView *manage_;
5349 SearchView *search_;
5354 @implementation Cydia
5357 if ([broken_ count] != 0) {
5358 int count = [broken_ count];
5360 UIActionSheet *sheet = [[[UIActionSheet alloc]
5361 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5362 buttons:[NSArray arrayWithObjects:
5364 @"Ignore (Temporary)",
5366 defaultButtonIndex:0
5371 [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."];
5372 [sheet popupAlertAnimated:YES];
5373 } else if (!Ignored_ && [essential_ count] != 0) {
5374 int count = [essential_ count];
5376 UIActionSheet *sheet = [[[UIActionSheet alloc]
5377 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5378 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5379 defaultButtonIndex:0
5384 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5385 [sheet popupAlertAnimated:YES];
5389 - (void) _reloadData {
5390 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5391 [hud setText:@"Reloading Data"];
5392 [overlay_ addSubview:hud];
5395 [database_ reloadData];
5399 [essential_ removeAllObjects];
5400 [broken_ removeAllObjects];
5402 NSArray *packages = [database_ packages];
5403 for (int i(0), e([packages count]); i != e; ++i) {
5404 Package *package = [packages objectAtIndex:i];
5406 [broken_ addObject:package];
5407 if ([package upgradableAndEssential:NO]) {
5408 if ([package essential])
5409 [essential_ addObject:package];
5415 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5416 [buttonbar_ setBadgeValue:badge forButton:3];
5417 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5418 [buttonbar_ setBadgeAnimated:YES forButton:3];
5419 [self setApplicationBadge:badge];
5421 [buttonbar_ setBadgeValue:nil forButton:3];
5422 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5423 [buttonbar_ setBadgeAnimated:NO forButton:3];
5424 [self removeApplicationBadge];
5429 if ([packages count] == 0);
5438 [hud removeFromSuperview];*/
5441 - (void) _saveConfig {
5443 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5448 - (void) updateData {
5451 /* XXX: this is just stupid */
5453 [install_ reloadData];
5455 [changes_ reloadData];
5457 [search_ reloadData];
5467 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5468 _assert(file != NULL);
5470 NSArray *keys = [Sources_ allKeys];
5472 for (int i(0), e([keys count]); i != e; ++i) {
5473 NSString *key = [keys objectAtIndex:i];
5474 NSDictionary *source = [Sources_ objectForKey:key];
5476 fprintf(file, "%s %s %s\n",
5477 [[source objectForKey:@"Type"] UTF8String],
5478 [[source objectForKey:@"URI"] UTF8String],
5479 [[source objectForKey:@"Distribution"] UTF8String]
5488 detachNewThreadSelector:@selector(update_)
5491 title:@"Updating Sources..."
5495 - (void) reloadData {
5496 @synchronized (self) {
5497 if (confirm_ == nil)
5503 pkgProblemResolver *resolver = [database_ resolver];
5505 resolver->InstallProtect();
5506 if (!resolver->Resolve(true))
5511 [database_ prepare];
5513 if ([database_ cache]->BrokenCount() == 0)
5514 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5516 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5517 NSArray *packages = [database_ packages];
5519 for (size_t i(0); i != [packages count]; ++i) {
5520 Package *package = [packages objectAtIndex:i];
5521 if ([package broken])
5522 [broken addObject:[package name]];
5525 UIActionSheet *sheet = [[[UIActionSheet alloc]
5526 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5527 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5528 defaultButtonIndex:0
5533 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5534 [sheet popupAlertAnimated:YES];
5540 - (void) installPackage:(Package *)package {
5541 @synchronized (self) {
5548 - (void) removePackage:(Package *)package {
5549 @synchronized (self) {
5556 - (void) distUpgrade {
5557 @synchronized (self) {
5558 [database_ upgrade];
5564 @synchronized (self) {
5572 [overlay_ removeFromSuperview];
5576 detachNewThreadSelector:@selector(perform)
5583 - (void) bootstrap_ {
5585 [database_ upgrade];
5586 [database_ prepare];
5587 [database_ perform];
5590 - (void) bootstrap {
5592 detachNewThreadSelector:@selector(bootstrap_)
5595 title:@"Bootstrap Install..."
5599 - (void) progressViewIsComplete:(ProgressView *)progress {
5600 @synchronized (self) {
5603 if (confirm_ != nil) {
5604 [underlay_ addSubview:overlay_];
5605 [confirm_ removeFromSuperview];
5612 - (void) setPage:(RVPage *)page {
5613 [page resetViewAnimated:NO];
5614 [page setDelegate:self];
5615 [book_ setPage:page];
5618 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5619 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5620 [browser loadURL:url];
5624 - (void) _setHomePage {
5625 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5628 - (void) buttonBarItemTapped:(id)sender {
5629 unsigned tag = [sender tag];
5631 [book_ resetViewAnimated:YES];
5633 } else if (tag_ == 2 && tag != 2)
5634 [install_ resetView];
5637 case 1: [self _setHomePage]; break;
5639 case 2: [self setPage:install_]; break;
5640 case 3: [self setPage:changes_]; break;
5641 case 4: [self setPage:manage_]; break;
5642 case 5: [self setPage:search_]; break;
5644 default: _assert(false);
5650 - (void) fixSpringBoard {
5651 pid_t pid = ExecFork();
5655 if (pid_t child = fork()) {
5656 waitpid(child, NULL, 0);
5658 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5659 perror("launchctl unload");
5663 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5664 perror("launchctl load");
5669 - (void) applicationWillSuspend {
5674 [self fixSpringBoard];
5678 [super applicationWillSuspend];
5681 - (void) askForSettings {
5682 UIActionSheet *role = [[[UIActionSheet alloc]
5683 initWithTitle:@"Who Are You?"
5684 buttons:[NSArray arrayWithObjects:
5685 @"User (Graphical Only)",
5686 @"Hacker (+ Command Line)",
5687 @"Developer (No Filters)",
5689 defaultButtonIndex:-1
5694 [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."];
5695 [role popupAlertAnimated:YES];
5700 [self setStatusBarShowsProgress:NO];
5703 [hud_ removeFromSuperview];
5707 pid_t pid = ExecFork();
5709 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5710 perror("launchctl stop");
5717 [self askForSettings];
5721 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5723 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5724 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5725 0, 0, screenrect.size.width, screenrect.size.height - 48
5726 ) database:database_];
5728 [book_ setDelegate:self];
5730 [overlay_ addSubview:book_];
5732 NSArray *buttonitems = [NSArray arrayWithObjects:
5733 [NSDictionary dictionaryWithObjectsAndKeys:
5734 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5735 @"home-up.png", kUIButtonBarButtonInfo,
5736 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5737 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5738 self, kUIButtonBarButtonTarget,
5739 @"Home", kUIButtonBarButtonTitle,
5740 @"0", kUIButtonBarButtonType,
5743 [NSDictionary dictionaryWithObjectsAndKeys:
5744 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5745 @"install-up.png", kUIButtonBarButtonInfo,
5746 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5747 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5748 self, kUIButtonBarButtonTarget,
5749 @"Sections", kUIButtonBarButtonTitle,
5750 @"0", kUIButtonBarButtonType,
5753 [NSDictionary dictionaryWithObjectsAndKeys:
5754 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5755 @"changes-up.png", kUIButtonBarButtonInfo,
5756 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5757 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5758 self, kUIButtonBarButtonTarget,
5759 @"Changes", kUIButtonBarButtonTitle,
5760 @"0", kUIButtonBarButtonType,
5763 [NSDictionary dictionaryWithObjectsAndKeys:
5764 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5765 @"manage-up.png", kUIButtonBarButtonInfo,
5766 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5767 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5768 self, kUIButtonBarButtonTarget,
5769 @"Manage", kUIButtonBarButtonTitle,
5770 @"0", kUIButtonBarButtonType,
5773 [NSDictionary dictionaryWithObjectsAndKeys:
5774 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5775 @"search-up.png", kUIButtonBarButtonInfo,
5776 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5777 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5778 self, kUIButtonBarButtonTarget,
5779 @"Search", kUIButtonBarButtonTitle,
5780 @"0", kUIButtonBarButtonType,
5784 buttonbar_ = [[UIToolbar alloc]
5786 withFrame:CGRectMake(
5787 0, screenrect.size.height - ButtonBarHeight_,
5788 screenrect.size.width, ButtonBarHeight_
5790 withItemList:buttonitems
5793 [buttonbar_ setDelegate:self];
5794 [buttonbar_ setBarStyle:1];
5795 [buttonbar_ setButtonBarTrackingMode:2];
5797 int buttons[5] = {1, 2, 3, 4, 5};
5798 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5799 [buttonbar_ showButtonGroup:0 withDuration:0];
5801 for (int i = 0; i != 5; ++i)
5802 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5803 i * 64 + 2, 1, 60, ButtonBarHeight_
5806 [buttonbar_ showSelectionForButton:1];
5807 [overlay_ addSubview:buttonbar_];
5809 [UIKeyboard initImplementationNow];
5810 CGSize keysize = [UIKeyboard defaultSize];
5811 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5812 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5813 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5814 [overlay_ addSubview:keyboard_];
5816 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5817 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5818 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5820 manage_ = (ManageView *) [[self
5821 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5822 withClass:[ManageView class]
5826 [underlay_ addSubview:overlay_];
5833 [self _setHomePage];
5836 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5837 NSString *context = [sheet context];
5838 if ([context isEqualToString:@"fixhalf"])
5841 @synchronized (self) {
5842 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5843 Package *broken = [broken_ objectAtIndex:i];
5846 NSString *id = [broken id];
5847 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5848 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5849 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5850 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5859 [broken_ removeAllObjects];
5866 else if ([context isEqualToString:@"role"]) {
5868 case 1: Role_ = @"User"; break;
5869 case 2: Role_ = @"Hacker"; break;
5870 case 3: Role_ = @"Developer"; break;
5877 bool reset = Settings_ != nil;
5879 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
5883 [Metadata_ setObject:Settings_ forKey:@"Settings"];
5891 } else if ([context isEqualToString:@"upgrade"])
5894 @synchronized (self) {
5895 for (int i = 0, e = [essential_ count]; i != e; ++i) {
5896 Package *essential = [essential_ objectAtIndex:i];
5897 [essential install];
5916 - (void) reorganize {
5917 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5918 system("/usr/libexec/cydia/free.sh");
5919 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
5923 - (void) applicationSuspend:(__GSEvent *)event {
5924 if (hud_ == nil && ![progress_ isRunning])
5925 [super applicationSuspend:event];
5928 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
5930 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
5933 - (void) _setSuspended:(BOOL)value {
5935 [super _setSuspended:value];
5938 - (UIProgressHUD *) addProgressHUD {
5939 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5941 [underlay_ addSubview:hud];
5945 - (void) applicationDidFinishLaunching:(id)unused {
5946 Font12_ = [[UIFont systemFontOfSize:12] retain];
5947 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
5948 Font14_ = [[UIFont systemFontOfSize:14] retain];
5949 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
5950 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
5952 _assert(pkgInitConfig(*_config));
5953 _assert(pkgInitSystem(*_config, _system));
5958 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
5959 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
5961 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5962 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
5964 [window_ orderFront:self];
5965 [window_ makeKey:self];
5966 [window_ _setHidden:NO];
5968 database_ = [[Database alloc] init];
5969 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
5970 [database_ setDelegate:progress_];
5971 [window_ setContentView:progress_];
5973 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
5974 [progress_ setContentView:underlay_];
5976 [progress_ resetView];
5979 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
5980 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
5981 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
5982 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
5983 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
5984 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
5986 [self setIdleTimerDisabled:YES];
5988 hud_ = [self addProgressHUD];
5989 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
5991 [self setStatusBarShowsProgress:YES];
5994 detachNewThreadSelector:@selector(reorganize)
6002 - (void) showKeyboard:(BOOL)show {
6003 CGSize keysize = [UIKeyboard defaultSize];
6004 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6005 CGRect keyup = keydown;
6006 keyup.origin.y -= keysize.height;
6008 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6009 [animation setSignificantRectFields:2];
6012 [animation setStartFrame:keydown];
6013 [animation setEndFrame:keyup];
6014 [keyboard_ activate];
6016 [animation setStartFrame:keyup];
6017 [animation setEndFrame:keydown];
6018 [keyboard_ deactivate];
6021 [[UIAnimator sharedAnimator]
6022 addAnimations:[NSArray arrayWithObjects:animation, nil]
6023 withDuration:KeyboardTime_
6028 - (void) slideUp:(UIActionSheet *)alert {
6030 [alert presentSheetFromButtonBar:buttonbar_];
6032 [alert presentSheetInView:overlay_];
6037 void AddPreferences(NSString *plist) {
6038 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6040 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6041 _assert(settings != NULL);
6042 NSMutableArray *items = [settings objectForKey:@"items"];
6046 for (size_t i(0); i != [items count]; ++i) {
6047 NSMutableDictionary *item([items objectAtIndex:i]);
6048 NSString *label = [item objectForKey:@"label"];
6049 if (label != nil && [label isEqualToString:@"Cydia"]) {
6056 for (size_t i(0); i != [items count]; ++i) {
6057 NSDictionary *item([items objectAtIndex:i]);
6058 NSString *label = [item objectForKey:@"label"];
6059 if (label != nil && [label isEqualToString:@"General"]) {
6060 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6061 @"CydiaSettings", @"bundle",
6062 @"PSLinkCell", @"cell",
6063 [NSNumber numberWithBool:YES], @"hasIcon",
6064 [NSNumber numberWithBool:YES], @"isController",
6066 nil] atIndex:(i + 1)];
6072 _assert([settings writeToFile:plist atomically:YES] == YES);
6079 id Alloc_(id self, SEL selector) {
6080 id object = alloc_(self, selector);
6081 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6086 id Dealloc_(id self, SEL selector) {
6087 id object = dealloc_(self, selector);
6088 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6092 int main(int argc, char *argv[]) {
6093 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6095 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6097 Home_ = NSHomeDirectory();
6100 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6101 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6102 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6103 Sounds_Keyboard_ = [keyboard boolValue];
6109 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6110 _assert(errno == ENOENT);
6111 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6112 _assert(errno == ENOENT);
6114 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6115 alloc_ = alloc->method_imp;
6116 alloc->method_imp = (IMP) &Alloc_;*/
6118 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6119 dealloc_ = dealloc->method_imp;
6120 dealloc->method_imp = (IMP) &Dealloc_;*/
6122 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6123 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6124 Firmware_ = strdup([prover UTF8String]);
6125 NSArray *versions = [prover componentsSeparatedByString:@"."];
6126 int count = [versions count];
6127 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6128 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6129 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6134 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6135 char *machine = new char[size];
6136 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6139 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6140 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6141 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6142 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6146 IOObjectRelease(service);
6149 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6150 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6152 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6153 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6155 Settings_ = [Metadata_ objectForKey:@"Settings"];
6157 Packages_ = [Metadata_ objectForKey:@"Packages"];
6158 Sections_ = [Metadata_ objectForKey:@"Sections"];
6159 Sources_ = [Metadata_ objectForKey:@"Sources"];
6162 if (Settings_ != nil)
6163 Role_ = [Settings_ objectForKey:@"Role"];
6165 if (Packages_ == nil) {
6166 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6167 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6170 if (Sections_ == nil) {
6171 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6172 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6175 if (Sources_ == nil) {
6176 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6177 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6180 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6181 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6183 if (access("/User", F_OK) != 0)
6184 system("/usr/libexec/cydia/firmware.sh");
6186 Locale_ = CFLocaleCopyCurrent();
6187 space_ = CGColorSpaceCreateDeviceRGB();
6189 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6190 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6191 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6192 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6193 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6194 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6195 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6197 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6199 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6201 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6203 CGColorSpaceRelease(space_);