1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #include <objc/objc.h>
40 #include <objc/runtime.h>
42 #include <CoreGraphics/CoreGraphics.h>
43 #include <GraphicsServices/GraphicsServices.h>
44 #include <Foundation/Foundation.h>
46 #include <WebCore/DOMHTML.h>
47 #import <QuartzCore/CALayer.h>
49 #import <UIKit/UIActionSheet.h>
50 #import <UIKit/UIAnimator.h>
51 #import <UIKit/UIApplication.h>
52 #import <UIKit/UIColor.h>
53 #import <UIKit/UIFieldEditor.h>
54 #import <UIKit/UIFrameAnimation.h>
55 #import <UIKit/UIHardware.h>
56 #import <UIKit/UIImage.h>
57 #import <UIKit/UIImageAndTextTableCell.h>
58 #import <UIKit/UIImageView.h>
59 #import <UIKit/UIKeyboard.h>
60 #import <UIKit/UIKeyboardImpl.h>
61 #import <UIKit/UINavigationBar.h>
62 #import <UIKit/UINavigationItem.h>
63 #import <UIKit/UIPreferencesTable.h>
64 #import <UIKit/UIPreferencesTableCell.h>
65 #import <UIKit/UIProgressBar.h>
66 #import <UIKit/UIProgressHUD.h>
67 #import <UIKit/UIProgressIndicator.h>
68 #import <UIKit/UIPushButton.h>
69 #import <UIKit/UISearchField.h>
70 #import <UIKit/UISimpleTableCell.h>
71 #import <UIKit/_UISwitchSlider.h>
72 #import <UIKit/UITableCell.h>
73 #import <UIKit/UITableColumn.h>
74 #import <UIKit/UITextField.h>
75 #import <UIKit/UITextInputTraits.h>
76 #import <UIKit/UITextLabel.h>
77 #import <UIKit/UITextView.h>
78 #import <UIKit/UIToolbar.h>
79 #import <UIKit/UITransitionView.h>
80 #import <UIKit/UIWebDocumentView.h>
81 #import <UIKit/UIWebView.h>
82 #import <UIKit/UIWindow.h>
84 #import <UIKit/UIView-Geometry.h>
85 #import <UIKit/UIView-Gestures.h>
86 #import <UIKit/UIView-Hierarchy.h>
87 #import <UIKit/UIView-Rendering.h>
89 #import <UIKit/UIWebDocumentView-Forms.h>
91 #import <UIKit/NSString-UIStringDrawing.h>
94 #import <UIKit/UIActionSheet-Private.h>
95 #import <UIKit/UIControl-UIControlPrivate.h>
96 #import <UIKit/UIImage-UIImageDeprecated.h>
97 #import <UIKit/UIImage-UIImagePrivate.h>
98 #import <UIKit/UINavigationBar-Static.h>
99 #import <UIKit/UIProgressHUD-Deprecated.h>
100 #import <UIKit/UIToolbar-UIButtonBarPrivate.h>
101 #import <UIKit/UIView-Deprecated.h>
102 #import <UIKit/UIWindow-Static.h>
105 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
107 #include <WebKit/WebFrame.h>
108 #include <WebKit/WebView.h>
113 #include <ext/stdio_filebuf.h>
115 #include <apt-pkg/acquire.h>
116 #include <apt-pkg/acquire-item.h>
117 #include <apt-pkg/algorithms.h>
118 #include <apt-pkg/cachefile.h>
119 #include <apt-pkg/clean.h>
120 #include <apt-pkg/configuration.h>
121 #include <apt-pkg/debmetaindex.h>
122 #include <apt-pkg/error.h>
123 #include <apt-pkg/init.h>
124 #include <apt-pkg/mmap.h>
125 #include <apt-pkg/pkgrecords.h>
126 #include <apt-pkg/sha1.h>
127 #include <apt-pkg/sourcelist.h>
128 #include <apt-pkg/sptr.h>
130 #include <sys/sysctl.h>
135 #include <mach-o/nlist.h>
145 #define UIWebView UIWebDocumentView
147 #import "BrowserView.h"
148 #import "ResetView.h"
149 #import "UICaboodle.h"
152 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
154 @interface WebView (Cydia)
155 - (void) _setLayoutInterval:(float)interval;
158 /* iPhoneOS 2.0 Compatibility {{{ */
160 @interface UICGColor : NSObject {
163 - (id) initWithCGColor:(CGColorRef)color;
169 + (id)systemFontOfSize:(float)fp8;
170 + (id)boldSystemFontOfSize:(float)fp8;
171 - (UIFont *) fontWithSize:(CGFloat)size;
174 @interface NSObject (iPhoneOS)
175 - (CGColorRef) cgColor;
176 - (CGColorRef) CGColor;
180 @implementation NSObject (iPhoneOS)
182 - (CGColorRef) cgColor {
183 return [self CGColor];
186 - (CGColorRef) CGColor {
187 return (CGColorRef) self;
191 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
196 @interface UITextView (iPhoneOS)
197 - (void) setTextSize:(float)size;
200 @implementation UITextView (iPhoneOS)
202 - (void) setTextSize:(float)size {
203 [self setFont:[[self font] fontWithSize:size]];
210 @interface UIApplication (IdleTimer)
211 - (void) setIdleTimerDisabled:(char)arg0;
214 extern "C" int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
216 extern NSString *kUIButtonBarButtonAction;
217 extern NSString *kUIButtonBarButtonInfo;
218 extern NSString *kUIButtonBarButtonInfoOffset;
219 extern NSString *kUIButtonBarButtonSelectedInfo;
220 extern NSString *kUIButtonBarButtonStyle;
221 extern NSString *kUIButtonBarButtonTag;
222 extern NSString *kUIButtonBarButtonTarget;
223 extern NSString *kUIButtonBarButtonTitle;
224 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
225 extern NSString *kUIButtonBarButtonTitleWidth;
226 extern NSString *kUIButtonBarButtonType;
229 kUIProgressIndicatorStyleLargeWhite = 0,
230 kUIProgressIndicatorStyleMediumWhite = 1,
231 kUIProgressIndicatorStyleSmallWhite = 3,
232 kUIProgressIndicatorStyleSmallBlack = 4
233 } UIProgressIndicatorStyle;
236 kUIControlEventMouseDown = 1 << 0,
237 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
238 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
239 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
240 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
241 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
242 } UIControlEventMasks;
244 @interface NSString (UIKit)
245 - (NSString *) stringByAddingPercentEscapes;
246 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
249 @interface NSString (Cydia)
250 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
251 - (NSComparisonResult) compareByPath:(NSString *)other;
254 @implementation NSString (Cydia)
256 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
257 char data[length + 1];
258 memcpy(data, bytes, length);
260 return [NSString stringWithUTF8String:data];
263 - (NSComparisonResult) compareByPath:(NSString *)other {
264 NSString *prefix = [self commonPrefixWithString:other options:0];
265 size_t length = [prefix length];
267 NSRange lrange = NSMakeRange(length, [self length] - length);
268 NSRange rrange = NSMakeRange(length, [other length] - length);
270 lrange = [self rangeOfString:@"/" options:0 range:lrange];
271 rrange = [other rangeOfString:@"/" options:0 range:rrange];
273 NSComparisonResult value;
275 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
276 value = NSOrderedSame;
277 else if (lrange.location == NSNotFound)
278 value = NSOrderedAscending;
279 else if (rrange.location == NSNotFound)
280 value = NSOrderedDescending;
282 value = NSOrderedSame;
284 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
285 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
286 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
287 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
289 NSComparisonResult result = [lpath compare:rpath];
290 return result == NSOrderedSame ? value : result;
295 /* Perl-Compatible RegEx {{{ */
305 Pcre(const char *regex) :
310 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
313 fprintf(stderr, "%d:%s\n", offset, error);
317 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
318 matches_ = new int[(capture_ + 1) * 3];
326 NSString *operator [](size_t match) {
327 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
330 bool operator ()(NSString *data) {
331 // XXX: length is for characters, not for bytes
332 return operator ()([data UTF8String], [data length]);
335 bool operator ()(const char *data, size_t size) {
337 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
341 /* Mime Addresses {{{ */
342 @interface Address : NSObject {
348 - (NSString *) address;
350 + (Address *) addressWithString:(NSString *)string;
351 - (Address *) initWithString:(NSString *)string;
354 @implementation Address
363 - (NSString *) name {
367 - (NSString *) address {
371 + (Address *) addressWithString:(NSString *)string {
372 return [[[Address alloc] initWithString:string] autorelease];
375 + (NSArray *) _attributeKeys {
376 return [NSArray arrayWithObjects:@"address", @"name", nil];
379 - (NSArray *) attributeKeys {
380 return [[self class] _attributeKeys];
383 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
384 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
387 - (Address *) initWithString:(NSString *)string {
388 if ((self = [super init]) != nil) {
389 const char *data = [string UTF8String];
390 size_t size = [string length];
392 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
394 if (address_r(data, size)) {
395 name_ = [address_r[1] retain];
396 address_ = [address_r[2] retain];
398 name_ = [[NSString alloc]
401 encoding:kCFStringEncodingUTF8
411 /* CoreGraphics Primitives {{{ */
422 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
425 Set(space, red, green, blue, alpha);
430 CGColorRelease(color_);
437 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
439 float color[] = {red, green, blue, alpha};
440 color_ = CGColorCreate(space, color);
443 operator CGColorRef() {
459 extern "C" void UISetColor(CGColorRef color);
461 /* Random Global Variables {{{ */
462 static const int PulseInterval_ = 50000;
463 static const int ButtonBarHeight_ = 48;
464 static const float KeyboardTime_ = 0.3f;
465 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
467 static CGColor Blue_;
468 static CGColor Blueish_;
469 static CGColor Black_;
470 static CGColor Clear_;
472 static CGColor White_;
473 static CGColor Gray_;
475 static NSString *App_;
476 static NSString *Home_;
477 static BOOL Sounds_Keyboard_;
479 static BOOL Advanced_;
481 static BOOL Ignored_;
483 static UIFont *Font12_;
484 static UIFont *Font12Bold_;
485 static UIFont *Font14_;
486 static UIFont *Font18Bold_;
487 static UIFont *Font22Bold_;
489 const char *Firmware_ = NULL;
490 const char *Machine_ = NULL;
491 const char *SerialNumber_ = NULL;
498 CGColorSpaceRef space_;
500 #define FW_LEAST(major, minor, bugfix) \
501 (major < Major_ || major == Major_ && \
502 (minor < Minor_ || minor == Minor_ && \
508 static NSDictionary *SectionMap_;
509 static NSMutableDictionary *Metadata_;
510 static _transient NSMutableDictionary *Settings_;
511 static _transient NSString *Role_;
512 static _transient NSMutableDictionary *Packages_;
513 static _transient NSMutableDictionary *Sections_;
514 static _transient NSMutableDictionary *Sources_;
515 static bool Changed_;
518 NSString *GetLastUpdate() {
519 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
522 return @"Never or Unknown";
524 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
525 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
527 CFRelease(formatter);
529 return [(NSString *) formatted autorelease];
532 /* Display Helpers {{{ */
533 inline float Interpolate(float begin, float end, float fraction) {
534 return (end - begin) * fraction + begin;
537 NSString *SizeString(double size) {
539 while (size > 1024) {
544 static const char *powers_[] = {"B", "kB", "MB", "GB"};
546 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
549 NSString *StripVersion(NSString *version) {
550 NSRange colon = [version rangeOfString:@":"];
551 if (colon.location != NSNotFound)
552 version = [version substringFromIndex:(colon.location + 1)];
556 static const float TextViewOffset_ = 22;
558 UITextView *GetTextView(NSString *value, float left, bool html) {
559 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
560 [text setEditable:NO];
561 [text setTextSize:16];
563 [text setHTML:value];
565 [text setText:value];
566 [text setEnabled:NO];
568 [text setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
570 CGRect frame = [text frame];
571 [text setFrame:frame];
572 CGRect rect = [text visibleTextRect];
573 frame.size.height = rect.size.height;
574 [text setFrame:frame];
579 NSString *Simplify(NSString *title) {
580 const char *data = [title UTF8String];
581 size_t size = [title length];
583 static Pcre square_r("^\\[(.*)\\]$");
584 if (square_r(data, size))
585 return Simplify(square_r[1]);
587 static Pcre paren_r("^\\((.*)\\)$");
588 if (paren_r(data, size))
589 return Simplify(paren_r[1]);
591 static Pcre title_r("^(.*?) \\(.*\\)$");
592 if (title_r(data, size))
593 return Simplify(title_r[1]);
599 bool isSectionVisible(NSString *section) {
600 NSDictionary *metadata = [Sections_ objectForKey:section];
601 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
602 return hidden == nil || ![hidden boolValue];
605 /* Delegate Prototypes {{{ */
609 @interface NSObject (ProgressDelegate)
612 @implementation NSObject(ProgressDelegate)
614 - (void) _setProgressError:(NSArray *)args {
615 [self performSelector:@selector(setProgressError:forPackage:)
616 withObject:[args objectAtIndex:0]
617 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
623 @protocol ProgressDelegate
624 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
625 - (void) setProgressTitle:(NSString *)title;
626 - (void) setProgressPercent:(float)percent;
627 - (void) addProgressOutput:(NSString *)output;
630 @protocol ConfigurationDelegate
631 - (void) repairWithSelector:(SEL)selector;
632 - (void) setConfigurationData:(NSString *)data;
635 @protocol CydiaDelegate
636 - (void) installPackage:(Package *)package;
637 - (void) removePackage:(Package *)package;
638 - (void) slideUp:(UIActionSheet *)alert;
639 - (void) distUpgrade;
642 - (void) askForSettings;
643 - (UIProgressHUD *) addProgressHUD;
647 /* Status Delegation {{{ */
649 public pkgAcquireStatus
652 _transient NSObject<ProgressDelegate> *delegate_;
660 void setDelegate(id delegate) {
661 delegate_ = delegate;
664 virtual bool MediaChange(std::string media, std::string drive) {
668 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
671 virtual void Fetch(pkgAcquire::ItemDesc &item) {
672 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
675 virtual void Done(pkgAcquire::ItemDesc &item) {
678 virtual void Fail(pkgAcquire::ItemDesc &item) {
680 item.Owner->Status == pkgAcquire::Item::StatIdle ||
681 item.Owner->Status == pkgAcquire::Item::StatDone
685 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
686 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
691 virtual bool Pulse(pkgAcquire *Owner) {
692 bool value = pkgAcquireStatus::Pulse(Owner);
695 double(CurrentBytes + CurrentItems) /
696 double(TotalBytes + TotalItems)
699 [delegate_ setProgressPercent:percent];
703 virtual void Start() {
706 virtual void Stop() {
710 /* Progress Delegation {{{ */
715 _transient id<ProgressDelegate> delegate_;
718 virtual void Update() {
719 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
720 [delegate_ setProgressPercent:(Percent / 100)];
729 void setDelegate(id delegate) {
730 delegate_ = delegate;
733 virtual void Done() {
734 [delegate_ setProgressPercent:1];
739 /* Database Interface {{{ */
740 @interface Database : NSObject {
742 pkgDepCache::Policy *policy_;
743 pkgRecords *records_;
744 pkgProblemResolver *resolver_;
745 pkgAcquire *fetcher_;
747 SPtr<pkgPackageManager> manager_;
748 pkgSourceList *list_;
750 NSMutableDictionary *sources_;
751 NSMutableArray *packages_;
753 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
762 - (void) _readCydia:(NSNumber *)fd;
763 - (void) _readStatus:(NSNumber *)fd;
764 - (void) _readOutput:(NSNumber *)fd;
768 - (Package *) packageWithName:(NSString *)name;
771 - (pkgCacheFile &) cache;
772 - (pkgDepCache::Policy *) policy;
773 - (pkgRecords *) records;
774 - (pkgProblemResolver *) resolver;
775 - (pkgAcquire &) fetcher;
776 - (NSArray *) packages;
777 - (NSArray *) sources;
786 - (void) updateWithStatus:(Status &)status;
788 - (void) setDelegate:(id)delegate;
789 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
793 /* Source Class {{{ */
794 @interface Source : NSObject {
795 NSString *description_;
800 NSString *distribution_;
804 NSString *defaultIcon_;
806 NSDictionary *record_;
810 - (Source *) initWithMetaIndex:(metaIndex *)index;
812 - (NSComparisonResult) compareByNameAndType:(Source *)source;
814 - (NSDictionary *) record;
818 - (NSString *) distribution;
824 - (NSString *) description;
825 - (NSString *) label;
826 - (NSString *) origin;
827 - (NSString *) version;
829 - (NSString *) defaultIcon;
833 @implementation Source
837 [distribution_ release];
840 if (description_ != nil)
841 [description_ release];
848 if (defaultIcon_ != nil)
849 [defaultIcon_ release];
856 + (NSArray *) _attributeKeys {
857 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
860 - (NSArray *) attributeKeys {
861 return [[self class] _attributeKeys];
864 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
865 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
868 - (Source *) initWithMetaIndex:(metaIndex *)index {
869 if ((self = [super init]) != nil) {
870 trusted_ = index->IsTrusted();
872 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
873 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
874 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
876 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
877 if (dindex != NULL) {
878 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
880 while (std::getline(release, line)) {
881 std::string::size_type colon(line.find(':'));
882 if (colon == std::string::npos)
885 std::string name(line.substr(0, colon));
886 std::string value(line.substr(colon + 1));
887 while (!value.empty() && value[0] == ' ')
888 value = value.substr(1);
890 if (name == "Default-Icon")
891 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
892 else if (name == "Description")
893 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
894 else if (name == "Label")
895 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
896 else if (name == "Origin")
897 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
898 else if (name == "Version")
899 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
903 record_ = [Sources_ objectForKey:[self key]];
905 record_ = [record_ retain];
909 - (NSComparisonResult) compareByNameAndType:(Source *)source {
910 NSDictionary *lhr = [self record];
911 NSDictionary *rhr = [source record];
914 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
916 NSString *lhs = [self name];
917 NSString *rhs = [source name];
919 if ([lhs length] != 0 && [rhs length] != 0) {
920 unichar lhc = [lhs characterAtIndex:0];
921 unichar rhc = [rhs characterAtIndex:0];
923 if (isalpha(lhc) && !isalpha(rhc))
924 return NSOrderedAscending;
925 else if (!isalpha(lhc) && isalpha(rhc))
926 return NSOrderedDescending;
929 return [lhs compare:rhs options:CompareOptions_];
932 - (NSDictionary *) record {
944 - (NSString *) distribution {
945 return distribution_;
948 - (NSString *) type {
953 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
956 - (NSString *) host {
957 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
960 - (NSString *) name {
961 return origin_ == nil ? [self host] : origin_;
964 - (NSString *) description {
968 - (NSString *) label {
969 return label_ == nil ? [self host] : label_;
972 - (NSString *) origin {
976 - (NSString *) version {
980 - (NSString *) defaultIcon {
986 /* Relationship Class {{{ */
987 @interface Relationship : NSObject {
998 @implementation Relationship
1006 - (NSString *) type {
1014 - (NSString *) name {
1021 /* Package Class {{{ */
1022 NSString *Scour(const char *field, const char *begin, const char *end) {
1023 size_t i(0), l(strlen(field));
1026 const char *name = begin + i;
1027 const char *colon = name + l;
1028 const char *value = colon + 1;
1033 memcmp(name, field, l) == 0
1035 while (value != end && value[0] == ' ')
1037 const char *line = std::find(value, end, '\n');
1038 while (line != value && line[-1] == ' ')
1041 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1043 begin = std::find(begin, end, '\n');
1051 @interface Package : NSObject {
1052 pkgCache::PkgIterator iterator_;
1053 _transient Database *database_;
1054 pkgCache::VerIterator version_;
1055 pkgCache::VerFileIterator file_;
1061 NSString *installed_;
1067 NSString *depiction_;
1068 NSString *homepage_;
1074 NSArray *relationships_;
1077 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1078 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1080 - (pkgCache::PkgIterator) iterator;
1082 - (NSString *) section;
1083 - (Address *) maintainer;
1085 - (NSString *) description;
1086 - (NSString *) index;
1090 - (NSString *) latest;
1091 - (NSString *) installed;
1094 - (BOOL) upgradableAndEssential:(BOOL)essential;
1097 - (BOOL) unfiltered;
1101 - (BOOL) halfConfigured;
1102 - (BOOL) halfInstalled;
1104 - (NSString *) mode;
1107 - (NSString *) name;
1108 - (NSString *) tagline;
1109 - (NSString *) icon;
1110 - (NSString *) homepage;
1111 - (NSString *) depiction;
1112 - (Address *) author;
1114 - (NSArray *) relationships;
1116 - (Source *) source;
1117 - (NSString *) role;
1119 - (BOOL) matches:(NSString *)text;
1121 - (bool) hasSupportingRole;
1122 - (BOOL) hasTag:(NSString *)tag;
1124 - (NSComparisonResult) compareByName:(Package *)package;
1125 - (NSComparisonResult) compareBySection:(Package *)package;
1126 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1127 - (NSComparisonResult) compareForChanges:(Package *)package;
1132 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1133 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1134 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1135 - (NSNumber *) isVisibleInSource:(Source *)source;
1139 @implementation Package
1146 if (installed_ != nil)
1147 [installed_ release];
1155 if (depiction_ != nil)
1156 [depiction_ release];
1157 if (homepage_ != nil)
1158 [homepage_ release];
1159 if (sponsor_ != nil)
1168 if (relationships_ != nil)
1169 [relationships_ release];
1174 + (NSArray *) _attributeKeys {
1175 return [NSArray arrayWithObjects:@"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"section", @"size", @"source", @"sponsor", @"tagline", nil];
1178 - (NSArray *) attributeKeys {
1179 return [[self class] _attributeKeys];
1182 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1183 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1186 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1187 if ((self = [super init]) != nil) {
1188 iterator_ = iterator;
1189 database_ = database;
1191 version_ = [database_ policy]->GetCandidateVer(iterator_);
1192 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1194 if (!version_.end())
1195 file_ = version_.FileList();
1197 pkgCache &cache([database_ cache]);
1198 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1201 pkgCache::VerIterator current = iterator_.CurrentVer();
1202 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1204 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1207 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1209 const char *begin, *end;
1210 parser->GetRec(begin, end);
1212 name_ = Scour("Name", begin, end);
1214 name_ = [name_ retain];
1215 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1216 icon_ = Scour("Icon", begin, end);
1218 icon_ = [icon_ retain];
1219 depiction_ = Scour("Depiction", begin, end);
1220 if (depiction_ != nil)
1221 depiction_ = [depiction_ retain];
1222 homepage_ = Scour("Homepage", begin, end);
1223 if (homepage_ == nil)
1224 homepage_ = Scour("Website", begin, end);
1225 if ([homepage_ isEqualToString:depiction_])
1227 if (homepage_ != nil)
1228 homepage_ = [homepage_ retain];
1229 NSString *sponsor = Scour("Sponsor", begin, end);
1231 sponsor_ = [[Address addressWithString:sponsor] retain];
1232 NSString *author = Scour("Author", begin, end);
1234 author_ = [[Address addressWithString:author] retain];
1235 NSString *tags = Scour("Tag", begin, end);
1237 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1241 for (int i(0), e([tags_ count]); i != e; ++i) {
1242 NSString *tag = [tags_ objectAtIndex:i];
1243 if ([tag hasPrefix:@"role::"]) {
1244 role_ = [[tag substringFromIndex:6] retain];
1249 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1250 if (metadata == nil || [metadata count] == 0) {
1251 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1255 [Packages_ setObject:metadata forKey:id_];
1261 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1262 return [[[Package alloc]
1263 initWithIterator:iterator
1268 - (pkgCache::PkgIterator) iterator {
1272 - (NSString *) section {
1273 const char *section = iterator_.Section();
1274 if (section == NULL)
1277 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1280 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1281 if (NSString *rename = [value objectForKey:@"Rename"]) {
1286 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1289 - (Address *) maintainer {
1292 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1293 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1297 return version_.end() ? 0 : version_->InstalledSize;
1300 - (NSString *) description {
1303 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1304 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1306 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1307 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1308 if ([lines count] < 2)
1311 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1312 for (size_t i(1); i != [lines count]; ++i) {
1313 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1314 [trimmed addObject:trim];
1317 return [trimmed componentsJoinedByString:@"\n"];
1320 - (NSString *) index {
1321 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1322 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1326 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1329 - (NSString *) latest {
1333 - (NSString *) installed {
1338 return !version_.end();
1341 - (BOOL) upgradableAndEssential:(BOOL)essential {
1342 pkgCache::VerIterator current = iterator_.CurrentVer();
1345 return essential && [self essential];
1347 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1348 return !candidate.end() && candidate != current;
1352 - (BOOL) essential {
1353 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1357 return [database_ cache][iterator_].InstBroken();
1360 - (BOOL) unfiltered {
1361 NSString *section = [self section];
1362 return section == nil || isSectionVisible(section);
1366 return [self hasSupportingRole] && [self unfiltered];
1370 unsigned char current = iterator_->CurrentState;
1371 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1374 - (BOOL) halfConfigured {
1375 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1378 - (BOOL) halfInstalled {
1379 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1383 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1384 return state.Mode != pkgDepCache::ModeKeep;
1387 - (NSString *) mode {
1388 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1390 switch (state.Mode) {
1391 case pkgDepCache::ModeDelete:
1392 if ((state.iFlags & pkgDepCache::Purge) != 0)
1397 case pkgDepCache::ModeKeep:
1398 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1403 case pkgDepCache::ModeInstall:
1404 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1405 return @"Reinstall";
1406 else switch (state.Status) {
1408 return @"Downgrade";
1414 return @"New Install";
1427 - (NSString *) name {
1428 return name_ == nil ? id_ : name_;
1431 - (NSString *) tagline {
1435 - (NSString *) icon {
1439 - (NSString *) homepage {
1443 - (NSString *) depiction {
1447 - (Address *) sponsor {
1451 - (Address *) author {
1455 - (NSArray *) relationships {
1456 return relationships_;
1459 - (Source *) source {
1461 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1468 - (NSString *) role {
1472 - (BOOL) matches:(NSString *)text {
1478 range = [[self id] rangeOfString:text options:NSCaseInsentiveSearch];
1479 if (range.location != NSNotFound)
1482 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1483 if (range.location != NSNotFound)
1486 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1487 if (range.location != NSNotFound)
1493 - (bool) hasSupportingRole {
1496 if ([role_ isEqualToString:@"enduser"])
1498 if ([Role_ isEqualToString:@"User"])
1500 if ([role_ isEqualToString:@"hacker"])
1502 if ([Role_ isEqualToString:@"Hacker"])
1504 if ([role_ isEqualToString:@"developer"])
1506 if ([Role_ isEqualToString:@"Developer"])
1511 - (BOOL) hasTag:(NSString *)tag {
1512 return tags_ == nil ? NO : [tags_ containsObject:tag];
1515 - (NSComparisonResult) compareByName:(Package *)package {
1516 NSString *lhs = [self name];
1517 NSString *rhs = [package name];
1519 if ([lhs length] != 0 && [rhs length] != 0) {
1520 unichar lhc = [lhs characterAtIndex:0];
1521 unichar rhc = [rhs characterAtIndex:0];
1523 if (isalpha(lhc) && !isalpha(rhc))
1524 return NSOrderedAscending;
1525 else if (!isalpha(lhc) && isalpha(rhc))
1526 return NSOrderedDescending;
1529 return [lhs compare:rhs options:CompareOptions_];
1532 - (NSComparisonResult) compareBySection:(Package *)package {
1533 NSString *lhs = [self section];
1534 NSString *rhs = [package section];
1536 if (lhs == NULL && rhs != NULL)
1537 return NSOrderedAscending;
1538 else if (lhs != NULL && rhs == NULL)
1539 return NSOrderedDescending;
1540 else if (lhs != NULL && rhs != NULL) {
1541 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1542 if (result != NSOrderedSame)
1546 return NSOrderedSame;
1549 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1550 NSString *lhs = [self section];
1551 NSString *rhs = [package section];
1553 if (lhs == NULL && rhs != NULL)
1554 return NSOrderedAscending;
1555 else if (lhs != NULL && rhs == NULL)
1556 return NSOrderedDescending;
1557 else if (lhs != NULL && rhs != NULL) {
1558 NSComparisonResult result = [lhs compare:rhs];
1559 if (result != NSOrderedSame)
1563 return [self compareByName:package];
1566 - (NSComparisonResult) compareForChanges:(Package *)package {
1567 BOOL lhs = [self upgradableAndEssential:YES];
1568 BOOL rhs = [package upgradableAndEssential:YES];
1571 return lhs ? NSOrderedAscending : NSOrderedDescending;
1573 switch ([[self seen] compare:[package seen]]) {
1574 case NSOrderedAscending:
1575 return NSOrderedDescending;
1578 case NSOrderedDescending:
1579 return NSOrderedAscending;
1585 return [self compareByName:package];
1589 pkgProblemResolver *resolver = [database_ resolver];
1590 resolver->Clear(iterator_);
1591 resolver->Protect(iterator_);
1592 pkgCacheFile &cache([database_ cache]);
1593 cache->MarkInstall(iterator_, false);
1594 pkgDepCache::StateCache &state((*cache)[iterator_]);
1595 if (!state.Install())
1596 cache->SetReInstall(iterator_, true);
1600 pkgProblemResolver *resolver = [database_ resolver];
1601 resolver->Clear(iterator_);
1602 resolver->Protect(iterator_);
1603 resolver->Remove(iterator_);
1604 [database_ cache]->MarkDelete(iterator_, true);
1607 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1608 return [NSNumber numberWithBool:(
1609 [self unfiltered] && [self matches:search]
1613 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1614 return [NSNumber numberWithBool:(
1615 (![number boolValue] || [self visible]) && [self installed] != nil
1619 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1620 NSString *section = [self section];
1622 return [NSNumber numberWithBool:(
1624 [self installed] == nil && (
1626 section == nil && [name length] == 0 ||
1627 [name isEqualToString:section]
1632 - (NSNumber *) isVisibleInSource:(Source *)source {
1633 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1638 /* Section Class {{{ */
1639 @interface Section : NSObject {
1645 - (NSComparisonResult) compareByName:(Section *)section;
1646 - (Section *) initWithName:(NSString *)name;
1647 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1648 - (NSString *) name;
1651 - (void) addToCount;
1655 @implementation Section
1662 - (NSComparisonResult) compareByName:(Section *)section {
1663 NSString *lhs = [self name];
1664 NSString *rhs = [section name];
1666 if ([lhs length] != 0 && [rhs length] != 0) {
1667 unichar lhc = [lhs characterAtIndex:0];
1668 unichar rhc = [rhs characterAtIndex:0];
1670 if (isalpha(lhc) && !isalpha(rhc))
1671 return NSOrderedAscending;
1672 else if (!isalpha(lhc) && isalpha(rhc))
1673 return NSOrderedDescending;
1676 return [lhs compare:rhs options:CompareOptions_];
1679 - (Section *) initWithName:(NSString *)name {
1680 return [self initWithName:name row:0];
1683 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1684 if ((self = [super init]) != nil) {
1685 name_ = [name retain];
1690 - (NSString *) name {
1702 - (void) addToCount {
1712 /* Database Implementation {{{ */
1713 @implementation Database
1720 - (void) _readCydia:(NSNumber *)fd {
1721 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1723 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1724 std::istream is(&ib);
1727 static Pcre finish_r("^finish:([^:]*)$");
1729 while (std::getline(is, line)) {
1730 const char *data(line.c_str());
1731 size_t size = line.size();
1732 fprintf(stderr, "C:%s\n", data);
1734 if (finish_r(data, size)) {
1735 NSString *finish = finish_r[1];
1736 int index = [Finishes_ indexOfObject:finish];
1737 if (index != INT_MAX && index > Finish_)
1746 - (void) _readStatus:(NSNumber *)fd {
1747 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1749 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1750 std::istream is(&ib);
1753 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1754 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1756 while (std::getline(is, line)) {
1757 const char *data(line.c_str());
1758 size_t size = line.size();
1759 fprintf(stderr, "S:%s\n", data);
1761 if (conffile_r(data, size)) {
1762 [delegate_ setConfigurationData:conffile_r[1]];
1763 } else if (strncmp(data, "status: ", 8) == 0) {
1764 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1765 [delegate_ setProgressTitle:string];
1766 } else if (pmstatus_r(data, size)) {
1767 std::string type([pmstatus_r[1] UTF8String]);
1768 NSString *id = pmstatus_r[2];
1770 float percent([pmstatus_r[3] floatValue]);
1771 [delegate_ setProgressPercent:(percent / 100)];
1773 NSString *string = pmstatus_r[4];
1775 if (type == "pmerror")
1776 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1777 withObject:[NSArray arrayWithObjects:string, id, nil]
1780 else if (type == "pmstatus")
1781 [delegate_ setProgressTitle:string];
1782 else if (type == "pmconffile")
1783 [delegate_ setConfigurationData:string];
1784 else _assert(false);
1785 } else _assert(false);
1792 - (void) _readOutput:(NSNumber *)fd {
1793 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1795 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1796 std::istream is(&ib);
1799 while (std::getline(is, line)) {
1800 fprintf(stderr, "O:%s\n", line.c_str());
1801 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1812 - (Package *) packageWithName:(NSString *)name {
1813 if (static_cast<pkgDepCache *>(cache_) == NULL)
1815 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1816 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1819 - (Database *) init {
1820 if ((self = [super init]) != nil) {
1827 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1828 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1832 _assert(pipe(fds) != -1);
1835 _config->Set("APT::Keep-Fds::", cydiafd_);
1836 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1839 detachNewThreadSelector:@selector(_readCydia:)
1841 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1844 _assert(pipe(fds) != -1);
1848 detachNewThreadSelector:@selector(_readStatus:)
1850 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1853 _assert(pipe(fds) != -1);
1854 _assert(dup2(fds[0], 0) != -1);
1855 _assert(close(fds[0]) != -1);
1857 input_ = fdopen(fds[1], "a");
1859 _assert(pipe(fds) != -1);
1860 _assert(dup2(fds[1], 1) != -1);
1861 _assert(close(fds[1]) != -1);
1864 detachNewThreadSelector:@selector(_readOutput:)
1866 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1871 - (pkgCacheFile &) cache {
1875 - (pkgDepCache::Policy *) policy {
1879 - (pkgRecords *) records {
1883 - (pkgProblemResolver *) resolver {
1887 - (pkgAcquire &) fetcher {
1891 - (NSArray *) packages {
1895 - (NSArray *) sources {
1896 return [sources_ allValues];
1899 - (void) reloadData {
1918 if (!cache_.Open(progress_, true)) {
1920 if (!_error->PopMessage(error))
1923 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1925 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1926 [delegate_ repairWithSelector:@selector(configure)];
1927 else if (error == "The package lists or status file could not be parsed or opened.")
1928 [delegate_ repairWithSelector:@selector(update)];
1929 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1930 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1931 // else if (error == "The list of sources could not be read.")
1932 else _assert(false);
1937 now_ = [[NSDate date] retain];
1939 policy_ = new pkgDepCache::Policy();
1940 records_ = new pkgRecords(cache_);
1941 resolver_ = new pkgProblemResolver(cache_);
1942 fetcher_ = new pkgAcquire(&status_);
1945 list_ = new pkgSourceList();
1946 _assert(list_->ReadMainList());
1948 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1949 _assert(pkgApplyStatus(cache_));
1951 if (cache_->BrokenCount() != 0) {
1952 _assert(pkgFixBroken(cache_));
1953 _assert(cache_->BrokenCount() == 0);
1954 _assert(pkgMinimizeUpgrade(cache_));
1957 [sources_ removeAllObjects];
1958 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1959 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1960 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1962 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1963 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1967 [packages_ removeAllObjects];
1968 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1969 if (Package *package = [Package packageWithIterator:iterator database:self])
1970 [packages_ addObject:package];
1972 [packages_ sortUsingSelector:@selector(compareByName:)];
1975 - (void) configure {
1976 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1977 system([dpkg UTF8String]);
1985 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1986 _assert(!_error->PendingError());
1989 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1992 public pkgArchiveCleaner
1995 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2000 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2002 while (_error->PopMessage(error))
2003 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
2008 pkgRecords records(cache_);
2010 lock_ = new FileFd();
2011 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2012 _assert(!_error->PendingError());
2015 // XXX: explain this with an error message
2016 _assert(list.ReadMainList());
2018 manager_ = (_system->CreatePM(cache_));
2019 _assert(manager_->GetArchives(fetcher_, &list, &records));
2020 _assert(!_error->PendingError());
2024 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2026 _assert(list.ReadMainList());
2027 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2028 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2031 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2036 bool failed = false;
2037 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2038 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2041 std::string uri = (*item)->DescURI();
2042 std::string error = (*item)->ErrorText;
2044 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
2047 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2048 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2059 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2061 if (_error->PendingError()) {
2066 if (result == pkgPackageManager::Failed) {
2071 if (result != pkgPackageManager::Completed) {
2076 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2078 _assert(list.ReadMainList());
2079 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2080 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2083 if (![before isEqualToArray:after])
2088 _assert(pkgDistUpgrade(cache_));
2092 [self updateWithStatus:status_];
2095 - (void) updateWithStatus:(Status &)status {
2097 _assert(list.ReadMainList());
2100 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2101 _assert(!_error->PendingError());
2103 pkgAcquire fetcher(&status);
2104 _assert(list.GetIndexes(&fetcher));
2106 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2107 bool failed = false;
2108 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2109 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2110 (*item)->Finished();
2114 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2115 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2116 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2119 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2124 - (void) setDelegate:(id)delegate {
2125 delegate_ = delegate;
2126 status_.setDelegate(delegate);
2127 progress_.setDelegate(delegate);
2130 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2131 pkgIndexFile *index(NULL);
2132 list_->FindIndex(file, index);
2133 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2139 /* Confirmation View {{{ */
2140 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2141 if ([packages count] == 0)
2144 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2145 [fields setObject:text forKey:key];
2147 CGColor blue(space_, 0, 0, 0.4, 1);
2148 [text setTextColor:[UIColor colorWithCGColor:blue]];
2151 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2152 if (!iterator.end())
2153 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2154 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2156 pkgCache::PkgIterator package(dep.TargetPkg());
2159 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2166 @protocol ConfirmationViewDelegate
2171 @interface ConfirmationView : UIView {
2172 Database *database_;
2174 UITransitionView *transition_;
2176 UINavigationBar *navbar_;
2177 UIPreferencesTable *table_;
2178 NSMutableDictionary *fields_;
2179 UIActionSheet *essential_;
2185 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2189 @implementation ConfirmationView
2192 [navbar_ setDelegate:nil];
2193 [transition_ setDelegate:nil];
2194 [table_ setDataSource:nil];
2196 [transition_ release];
2201 if (essential_ != nil)
2202 [essential_ release];
2207 [transition_ transition:7 toView:nil];
2211 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2212 if (from != nil && to == nil)
2213 [self removeFromSuperview];
2216 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2219 if (essential_ != nil)
2220 [essential_ popupAlertAnimated:YES];
2224 [delegate_ confirm];
2234 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2235 NSString *context = [sheet context];
2237 if ([context isEqualToString:@"remove"])
2245 [delegate_ confirm];
2250 else if ([context isEqualToString:@"unable"])
2256 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2260 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2262 case 0: return @"Statistics";
2263 case 1: return @"Modifications";
2265 default: _assert(false);
2269 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2272 case 1: return [fields_ count];
2274 default: _assert(false);
2278 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2279 if (group != 1 || row == -1)
2282 _assert(size_t(row) < [fields_ count]);
2283 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2287 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2288 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2289 [cell setShowSelection:NO];
2292 case 0: switch (row) {
2294 [cell setTitle:@"Downloading"];
2295 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2299 [cell setTitle:@"Resuming At"];
2300 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2304 double size([database_ cache]->UsrSize());
2307 [cell setTitle:@"Disk Freeing"];
2308 [cell setValue:SizeString(-size)];
2310 [cell setTitle:@"Disk Using"];
2311 [cell setValue:SizeString(size)];
2315 default: _assert(false);
2319 _assert(size_t(row) < [fields_ count]);
2320 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2321 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2324 default: _assert(false);
2330 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2331 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2332 database_ = database;
2333 delegate_ = delegate;
2335 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2336 [self addSubview:transition_];
2338 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2340 CGSize navsize = [UINavigationBar defaultSize];
2341 CGRect navrect = {{0, 0}, navsize};
2342 CGRect bounds = [overlay_ bounds];
2344 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2345 [navbar_ setDelegate:self];
2347 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2348 [navbar_ pushNavigationItem:navitem];
2349 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2351 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2353 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2354 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2355 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2356 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2357 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2361 pkgDepCache::Policy *policy([database_ policy]);
2363 pkgCacheFile &cache([database_ cache]);
2364 NSArray *packages = [database_ packages];
2365 for (size_t i(0), e = [packages count]; i != e; ++i) {
2366 Package *package = [packages objectAtIndex:i];
2367 pkgCache::PkgIterator iterator = [package iterator];
2368 pkgDepCache::StateCache &state(cache[iterator]);
2370 NSString *name([package name]);
2372 if (state.NewInstall())
2373 [installing addObject:name];
2374 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2375 [reinstalling addObject:name];
2376 else if (state.Upgrade())
2377 [upgrading addObject:name];
2378 else if (state.Downgrade())
2379 [downgrading addObject:name];
2380 else if (state.Delete()) {
2381 if ([package essential])
2383 [removing addObject:name];
2386 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2387 substrate_ |= DepSubstrate(iterator.CurrentVer());
2392 else if (Advanced_ || true) {
2393 essential_ = [[UIActionSheet alloc]
2394 initWithTitle:@"Removing Essentials"
2395 buttons:[NSArray arrayWithObjects:
2396 @"Cancel Operation (Safe)",
2397 @"Force Removal (Unsafe)",
2399 defaultButtonIndex:0
2405 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2407 [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."];
2409 essential_ = [[UIActionSheet alloc]
2410 initWithTitle:@"Unable to Comply"
2411 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2412 defaultButtonIndex:0
2417 [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."];
2420 AddTextView(fields_, installing, @"Installing");
2421 AddTextView(fields_, reinstalling, @"Reinstalling");
2422 AddTextView(fields_, upgrading, @"Upgrading");
2423 AddTextView(fields_, downgrading, @"Downgrading");
2424 AddTextView(fields_, removing, @"Removing");
2426 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2427 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2430 [table_ setReusesTableCells:YES];
2431 [table_ setDataSource:self];
2432 [table_ reloadData];
2434 [overlay_ addSubview:navbar_];
2435 [overlay_ addSubview:table_];
2437 [view addSubview:self];
2439 [transition_ setDelegate:self];
2441 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2442 [transition_ transition:0 toView:blank];
2443 [transition_ transition:3 toView:overlay_];
2450 /* Progress Data {{{ */
2451 @interface ProgressData : NSObject {
2457 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2464 @implementation ProgressData
2466 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2467 if ((self = [super init]) != nil) {
2468 selector_ = selector;
2488 /* Progress View {{{ */
2489 @interface ProgressView : UIView <
2490 ConfigurationDelegate,
2493 _transient Database *database_;
2495 UIView *background_;
2496 UITransitionView *transition_;
2498 UINavigationBar *navbar_;
2499 UIProgressBar *progress_;
2500 UITextView *output_;
2501 UITextLabel *status_;
2502 UIPushButton *close_;
2505 SHA1SumValue springlist_;
2508 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2510 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2511 - (void) setContentView:(UIView *)view;
2514 - (void) _retachThread;
2515 - (void) _detachNewThreadData:(ProgressData *)data;
2516 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2522 @protocol ProgressViewDelegate
2523 - (void) progressViewIsComplete:(ProgressView *)sender;
2526 @implementation ProgressView
2529 [transition_ setDelegate:nil];
2530 [navbar_ setDelegate:nil];
2533 if (background_ != nil)
2534 [background_ release];
2535 [transition_ release];
2538 [progress_ release];
2545 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2546 if (bootstrap_ && from == overlay_ && to == view_)
2550 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2551 if ((self = [super initWithFrame:frame]) != nil) {
2552 database_ = database;
2553 delegate_ = delegate;
2555 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2556 [transition_ setDelegate:self];
2558 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2561 [overlay_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2563 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2564 [background_ setBackgroundColor:[UIColor colorWithCGColor:Black_]];
2565 [self addSubview:background_];
2568 [self addSubview:transition_];
2570 CGSize navsize = [UINavigationBar defaultSize];
2571 CGRect navrect = {{0, 0}, navsize};
2573 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2574 [overlay_ addSubview:navbar_];
2576 [navbar_ setBarStyle:1];
2577 [navbar_ setDelegate:self];
2579 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2580 [navbar_ pushNavigationItem:navitem];
2582 CGRect bounds = [overlay_ bounds];
2583 CGSize prgsize = [UIProgressBar defaultSize];
2586 (bounds.size.width - prgsize.width) / 2,
2587 bounds.size.height - prgsize.height - 20
2590 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2591 [progress_ setStyle:0];
2593 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2595 bounds.size.height - prgsize.height - 50,
2596 bounds.size.width - 20,
2600 [status_ setColor:[UIColor colorWithCGColor:White_]];
2601 [status_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2603 [status_ setCentersHorizontally:YES];
2604 //[status_ setFont:font];
2606 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2608 navrect.size.height + 20,
2609 bounds.size.width - 20,
2610 bounds.size.height - navsize.height - 62 - navrect.size.height
2613 //[output_ setTextFont:@"Courier New"];
2614 [output_ setTextSize:12];
2616 [output_ setTextColor:[UIColor colorWithCGColor:White_]];
2617 [output_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
2619 [output_ setMarginTop:0];
2620 [output_ setAllowsRubberBanding:YES];
2621 [output_ setEditable:NO];
2623 [overlay_ addSubview:output_];
2625 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2627 bounds.size.height - prgsize.height - 50,
2628 bounds.size.width - 20,
2632 [close_ setAutosizesToFit:NO];
2633 [close_ setDrawsShadow:YES];
2634 [close_ setStretchBackground:YES];
2635 [close_ setEnabled:YES];
2637 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2638 [close_ setTitleFont:bold];
2640 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2641 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2642 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2646 - (void) setContentView:(UIView *)view {
2647 view_ = [view retain];
2650 - (void) resetView {
2651 [transition_ transition:6 toView:view_];
2654 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2655 NSString *context = [sheet context];
2656 if ([context isEqualToString:@"conffile"]) {
2657 FILE *input = [database_ input];
2661 fprintf(input, "N\n");
2665 fprintf(input, "Y\n");
2676 - (void) closeButtonPushed {
2679 [delegate_ progressViewIsComplete:self];
2684 [delegate_ suspendWithAnimation:YES];
2688 system("launchctl stop com.apple.SpringBoard");
2692 system("launchctl unload /System/Library/LaunchDaemons/com.apple.SpringBoard.plist; launchctl load /System/Library/LaunchDaemons/com.apple.SpringBoard.plist");
2701 - (void) _retachThread {
2702 UINavigationItem *item = [navbar_ topItem];
2703 [item setTitle:@"Complete"];
2705 [overlay_ addSubview:close_];
2706 [progress_ removeFromSuperview];
2707 [status_ removeFromSuperview];
2710 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2711 MMap mmap(file, MMap::ReadOnly);
2713 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2714 if (!(springlist_ == sha1.Result()))
2719 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2720 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2721 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2722 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2723 case 4: [close_ setTitle:@"Reboot Device"]; break;
2727 notify_post("com.apple.mobile.application_installed");
2730 [delegate_ setStatusBarShowsProgress:NO];
2735 - (void) _detachNewThreadData:(ProgressData *)data {
2736 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2738 [[data target] performSelector:[data selector] withObject:[data object]];
2741 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2746 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2747 UINavigationItem *item = [navbar_ topItem];
2748 [item setTitle:title];
2750 [status_ setText:nil];
2751 [output_ setText:@""];
2752 [progress_ setProgress:0];
2754 [close_ removeFromSuperview];
2755 [overlay_ addSubview:progress_];
2756 [overlay_ addSubview:status_];
2758 [delegate_ setStatusBarShowsProgress:YES];
2762 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2763 MMap mmap(file, MMap::ReadOnly);
2765 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2766 springlist_ = sha1.Result();
2769 [transition_ transition:6 toView:overlay_];
2772 detachNewThreadSelector:@selector(_detachNewThreadData:)
2774 withObject:[[ProgressData alloc]
2775 initWithSelector:selector
2782 - (void) repairWithSelector:(SEL)selector {
2784 detachNewThreadSelector:selector
2787 title:@"Repairing..."
2791 - (void) setConfigurationData:(NSString *)data {
2793 performSelectorOnMainThread:@selector(_setConfigurationData:)
2799 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2800 Package *package = id == nil ? nil : [database_ packageWithName:id];
2802 UIActionSheet *sheet = [[[UIActionSheet alloc]
2803 initWithTitle:(package == nil ? @"Source Error" : [package name])
2804 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2805 defaultButtonIndex:0
2810 [sheet setBodyText:error];
2811 [sheet popupAlertAnimated:YES];
2814 - (void) setProgressTitle:(NSString *)title {
2816 performSelectorOnMainThread:@selector(_setProgressTitle:)
2822 - (void) setProgressPercent:(float)percent {
2824 performSelectorOnMainThread:@selector(_setProgressPercent:)
2825 withObject:[NSNumber numberWithFloat:percent]
2830 - (void) addProgressOutput:(NSString *)output {
2832 performSelectorOnMainThread:@selector(_addProgressOutput:)
2838 - (void) _setConfigurationData:(NSString *)data {
2839 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2841 _assert(conffile_r(data));
2843 NSString *ofile = conffile_r[1];
2844 //NSString *nfile = conffile_r[2];
2846 UIActionSheet *sheet = [[[UIActionSheet alloc]
2847 initWithTitle:@"Configuration Upgrade"
2848 buttons:[NSArray arrayWithObjects:
2849 @"Keep My Old Copy",
2850 @"Accept The New Copy",
2851 // XXX: @"See What Changed",
2853 defaultButtonIndex:0
2858 [sheet setBodyText:[NSString stringWithFormat:
2859 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2862 [sheet popupAlertAnimated:YES];
2865 - (void) _setProgressTitle:(NSString *)title {
2866 [status_ setText:[title stringByAppendingString:@"..."]];
2869 - (void) _setProgressPercent:(NSNumber *)percent {
2870 [progress_ setProgress:[percent floatValue]];
2873 - (void) _addProgressOutput:(NSString *)output {
2874 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2875 CGSize size = [output_ contentSize];
2876 CGRect rect = {{0, size.height}, {size.width, 0}};
2877 [output_ scrollRectToVisible:rect animated:YES];
2880 - (BOOL) isRunning {
2887 /* Package Cell {{{ */
2888 @interface PackageCell : UISimpleTableCell {
2891 NSString *description_;
2893 //UIImageView *trusted_;
2895 UIImageView *badge_;
2896 UITextLabel *status_;
2900 - (PackageCell *) init;
2901 - (void) setPackage:(Package *)package;
2903 + (int) heightForPackage:(Package *)package;
2907 @implementation PackageCell
2909 - (void) clearPackage {
2920 if (description_ != nil) {
2921 [description_ release];
2925 if (source_ != nil) {
2932 [self clearPackage];
2937 //[trusted_ release];
2941 - (PackageCell *) init {
2942 if ((self = [super init]) != nil) {
2944 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2946 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2947 [status_ setBackgroundColor:Clear_];
2948 [status_ setFont:small];
2953 - (void) setPackage:(Package *)package {
2954 [self clearPackage];
2956 Source *source = [package source];
2959 if (NSString *icon = [package icon])
2960 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2961 if (icon_ == nil) if (NSString *section = [package section])
2962 icon_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, Simplify(section)]];
2963 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2964 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2966 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2968 icon_ = [icon_ retain];
2970 name_ = [[package name] retain];
2971 description_ = [[package tagline] retain];
2973 NSString *label = nil;
2974 bool trusted = false;
2976 if (source != nil) {
2977 label = [source label];
2978 trusted = [source trusted];
2979 } else if ([[package id] isEqualToString:@"firmware"])
2982 label = @"Unknown/Local";
2984 NSString *from = [NSString stringWithFormat:@"from %@", label];
2986 NSString *section = Simplify([package section]);
2987 if (section != nil && ![section isEqualToString:label])
2988 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2990 source_ = [from retain];
2993 [badge_ removeFromSuperview];
2994 [status_ removeFromSuperview];
2996 if (NSString *mode = [package mode]) {
2997 [badge_ setImage:[UIImage applicationImageNamed:
2998 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3001 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3002 [status_ setColor:Blueish_];
3003 } else if ([package half]) {
3004 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3005 [status_ setText:@"Package Damaged"];
3006 [status_ setColor:Red_];
3008 [badge_ setImage:nil];
3009 [status_ setText:nil];
3013 [self addSubview:badge_];
3014 [self addSubview:status_];
3019 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3021 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3028 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3029 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3033 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3035 [super drawContentInRect:rect selected:selected];
3038 + (int) heightForPackage:(Package *)package {
3039 NSString *tagline([package tagline]);
3040 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3042 if ([package hasMode] || [package half])
3051 /* Section Cell {{{ */
3052 @interface SectionCell : UISimpleTableCell {
3057 _UISwitchSlider *switch_;
3062 - (void) setSection:(Section *)section editing:(BOOL)editing;
3066 @implementation SectionCell
3068 - (void) clearSection {
3069 if (section_ != nil) {
3079 if (count_ != nil) {
3086 [self clearSection];
3093 if ((self = [super init]) != nil) {
3094 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3096 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3097 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3101 - (void) onSwitch:(id)sender {
3102 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3103 if (metadata == nil) {
3104 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3105 [Sections_ setObject:metadata forKey:section_];
3109 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3112 - (void) setSection:(Section *)section editing:(BOOL)editing {
3113 if (editing != editing_) {
3115 [switch_ removeFromSuperview];
3117 [self addSubview:switch_];
3121 [self clearSection];
3123 if (section == nil) {
3124 name_ = [@"All Packages" retain];
3127 section_ = [section name];
3128 if (section_ != nil)
3129 section_ = [section_ retain];
3130 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3131 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3134 [switch_ setValue:isSectionVisible(section_) animated:NO];
3138 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3139 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3146 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3148 CGSize size = [count_ sizeWithFont:Font14_];
3152 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3154 [super drawContentInRect:rect selected:selected];
3160 /* File Table {{{ */
3161 @interface FileTable : RVPage {
3162 _transient Database *database_;
3165 NSMutableArray *files_;
3169 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3170 - (void) setPackage:(Package *)package;
3174 @implementation FileTable
3177 if (package_ != nil)
3186 - (int) numberOfRowsInTable:(UITable *)table {
3187 return files_ == nil ? 0 : [files_ count];
3190 - (float) table:(UITable *)table heightForRow:(int)row {
3194 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3195 if (reusing == nil) {
3196 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3197 UIFont *font = [UIFont systemFontOfSize:16];
3198 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3200 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3204 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3208 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3209 if ((self = [super initWithBook:book]) != nil) {
3210 database_ = database;
3212 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3214 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3215 [self addSubview:list_];
3217 UITableColumn *column = [[[UITableColumn alloc]
3218 initWithTitle:@"Name"
3220 width:[self frame].size.width
3223 [list_ setDataSource:self];
3224 [list_ setSeparatorStyle:1];
3225 [list_ addTableColumn:column];
3226 [list_ setDelegate:self];
3227 [list_ setReusesTableCells:YES];
3231 - (void) setPackage:(Package *)package {
3232 if (package_ != nil) {
3233 [package_ autorelease];
3242 [files_ removeAllObjects];
3244 if (package != nil) {
3245 package_ = [package retain];
3246 name_ = [[package id] retain];
3248 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3251 std::ifstream fin([path UTF8String]);
3253 while (std::getline(fin, line))
3254 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3257 if ([files_ count] != 0) {
3258 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3259 [files_ removeObjectAtIndex:0];
3260 [files_ sortUsingSelector:@selector(compareByPath:)];
3262 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3263 [stack addObject:@"/"];
3265 for (int i(0), e([files_ count]); i != e; ++i) {
3266 NSString *file = [files_ objectAtIndex:i];
3267 while (![file hasPrefix:[stack lastObject]])
3268 [stack removeLastObject];
3269 NSString *directory = [stack lastObject];
3270 [stack addObject:[file stringByAppendingString:@"/"]];
3271 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3272 ([stack count] - 2) * 3, "",
3273 [file substringFromIndex:[directory length]]
3282 - (void) resetViewAnimated:(BOOL)animated {
3283 [list_ resetViewAnimated:animated];
3286 - (void) reloadData {
3287 [self setPackage:[database_ packageWithName:name_]];
3288 [self reloadButtons];
3291 - (NSString *) title {
3292 return @"Installed Files";
3295 - (NSString *) backButtonTitle {
3301 /* Package View {{{ */
3302 @interface PackageView : BrowserView {
3305 NSMutableArray *buttons_;
3308 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3309 - (void) setPackage:(Package *)package;
3313 @implementation PackageView
3316 if (package_ != nil)
3324 - (void) _clickButtonWithName:(NSString *)name {
3325 if ([name isEqualToString:@"Install"])
3326 [delegate_ installPackage:package_];
3327 else if ([name isEqualToString:@"Reinstall"])
3328 [delegate_ installPackage:package_];
3329 else if ([name isEqualToString:@"Remove"])
3330 [delegate_ removePackage:package_];
3331 else if ([name isEqualToString:@"Upgrade"])
3332 [delegate_ installPackage:package_];
3333 else _assert(false);
3336 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3337 int count = [buttons_ count];
3338 _assert(count != 0);
3339 _assert(button <= count + 1);
3341 if (count != button - 1)
3342 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3347 #include "internals.h"
3349 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3350 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3351 return [super webView:sender didFinishLoadForFrame:frame];
3354 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3355 [window setValue:package_ forKey:@"package"];
3358 - (void) _rightButtonClicked {
3359 /*[super _rightButtonClicked];
3362 int count = [buttons_ count];
3363 _assert(count != 0);
3366 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3368 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3369 [buttons addObjectsFromArray:buttons_];
3370 [buttons addObject:@"Cancel"];
3372 [delegate_ slideUp:[[[UIActionSheet alloc]
3375 defaultButtonIndex:2
3382 - (NSString *) _rightButtonTitle {
3383 int count = [buttons_ count];
3384 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3387 - (NSString *) title {
3391 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3392 if ((self = [super initWithBook:book database:database]) != nil) {
3393 database_ = database;
3394 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3398 - (void) setPackage:(Package *)package {
3399 if (package_ != nil) {
3400 [package_ autorelease];
3409 [buttons_ removeAllObjects];
3411 if (package != nil) {
3412 package_ = [package retain];
3413 name_ = [[package id] retain];
3415 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3417 if ([package_ source] == nil);
3418 else if ([package_ upgradableAndEssential:NO])
3419 [buttons_ addObject:@"Upgrade"];
3420 else if ([package_ installed] == nil)
3421 [buttons_ addObject:@"Install"];
3423 [buttons_ addObject:@"Reinstall"];
3424 if ([package_ installed] != nil)
3425 [buttons_ addObject:@"Remove"];
3429 - (void) reloadData {
3430 [self setPackage:[database_ packageWithName:name_]];
3431 [self reloadButtons];
3436 /* Package Table {{{ */
3437 @interface PackageTable : RVPage {
3438 _transient Database *database_;
3442 NSMutableArray *packages_;
3443 NSMutableArray *sections_;
3444 UISectionList *list_;
3447 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3449 - (void) setDelegate:(id)delegate;
3450 - (void) setObject:(id)object;
3452 - (void) reloadData;
3453 - (void) resetCursor;
3455 - (UISectionList *) list;
3457 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3461 @implementation PackageTable
3464 [list_ setDataSource:nil];
3469 [packages_ release];
3470 [sections_ release];
3475 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3476 return [sections_ count];
3479 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3480 return [[sections_ objectAtIndex:section] name];
3483 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3484 return [[sections_ objectAtIndex:section] row];
3487 - (int) numberOfRowsInTable:(UITable *)table {
3488 return [packages_ count];
3491 - (float) table:(UITable *)table heightForRow:(int)row {
3492 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3495 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3497 reusing = [[[PackageCell alloc] init] autorelease];
3498 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3502 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3506 - (void) tableRowSelected:(NSNotification *)notification {
3507 int row = [[notification object] selectedRow];
3511 Package *package = [packages_ objectAtIndex:row];
3512 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3513 [view setDelegate:delegate_];
3514 [view setPackage:package];
3515 [book_ pushPage:view];
3518 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3519 if ((self = [super initWithBook:book]) != nil) {
3520 database_ = database;
3521 title_ = [title retain];
3523 object_ = object == nil ? nil : [object retain];
3525 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3526 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3528 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3529 [list_ setDataSource:self];
3531 UITableColumn *column = [[[UITableColumn alloc]
3532 initWithTitle:@"Name"
3534 width:[self frame].size.width
3537 UITable *table = [list_ table];
3538 [table setSeparatorStyle:1];
3539 [table addTableColumn:column];
3540 [table setDelegate:self];
3541 [table setReusesTableCells:YES];
3543 [self addSubview:list_];
3548 - (void) setDelegate:(id)delegate {
3549 delegate_ = delegate;
3552 - (void) setObject:(id)object {
3558 object_ = [object retain];
3561 - (void) reloadData {
3562 NSArray *packages = [database_ packages];
3564 [packages_ removeAllObjects];
3565 [sections_ removeAllObjects];
3567 for (size_t i(0); i != [packages count]; ++i) {
3568 Package *package([packages objectAtIndex:i]);
3569 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3570 [packages_ addObject:package];
3573 Section *section = nil;
3575 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3576 Package *package = [packages_ objectAtIndex:offset];
3577 NSString *name = [package index];
3579 if (section == nil || ![[section name] isEqualToString:name]) {
3580 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3581 [sections_ addObject:section];
3584 [section addToCount];
3590 - (NSString *) title {
3594 - (void) resetViewAnimated:(BOOL)animated {
3595 [list_ resetViewAnimated:animated];
3598 - (void) resetCursor {
3599 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3602 - (UISectionList *) list {
3606 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3607 [list_ setShouldHideHeaderInShortLists:hide];
3613 /* Add Source View {{{ */
3614 @interface AddSourceView : RVPage {
3615 _transient Database *database_;
3618 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3622 @implementation AddSourceView
3624 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3625 if ((self = [super initWithBook:book]) != nil) {
3626 database_ = database;
3632 /* Source Cell {{{ */
3633 @interface SourceCell : UITableCell {
3636 NSString *description_;
3642 - (SourceCell *) initWithSource:(Source *)source;
3646 @implementation SourceCell
3651 [description_ release];
3656 - (SourceCell *) initWithSource:(Source *)source {
3657 if ((self = [super init]) != nil) {
3659 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3661 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3662 icon_ = [icon_ retain];
3664 origin_ = [[source name] retain];
3665 label_ = [[source uri] retain];
3666 description_ = [[source description] retain];
3670 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3672 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3679 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3683 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3687 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3689 [super drawContentInRect:rect selected:selected];
3694 /* Source Table {{{ */
3695 @interface SourceTable : RVPage {
3696 _transient Database *database_;
3697 UISectionList *list_;
3698 NSMutableArray *sources_;
3699 UIActionSheet *alert_;
3703 UIProgressHUD *hud_;
3706 //NSURLConnection *installer_;
3707 NSURLConnection *trivial_bz2_;
3708 NSURLConnection *trivial_gz_;
3709 //NSURLConnection *automatic_;
3714 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3718 @implementation SourceTable
3720 - (void) _deallocConnection:(NSURLConnection *)connection {
3721 if (connection != nil) {
3722 [connection cancel];
3723 //[connection setDelegate:nil];
3724 [connection release];
3729 [[list_ table] setDelegate:nil];
3730 [list_ setDataSource:nil];
3739 //[self _deallocConnection:installer_];
3740 [self _deallocConnection:trivial_gz_];
3741 [self _deallocConnection:trivial_bz2_];
3742 //[self _deallocConnection:automatic_];
3749 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3750 return offset_ == 0 ? 1 : 2;
3753 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3754 switch (section + (offset_ == 0 ? 1 : 0)) {
3755 case 0: return @"Entered by User";
3756 case 1: return @"Installed by Packages";
3764 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3765 switch (section + (offset_ == 0 ? 1 : 0)) {
3767 case 1: return offset_;
3775 - (int) numberOfRowsInTable:(UITable *)table {
3776 return [sources_ count];
3779 - (float) table:(UITable *)table heightForRow:(int)row {
3780 Source *source = [sources_ objectAtIndex:row];
3781 return [source description] == nil ? 56 : 73;
3784 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3785 Source *source = [sources_ objectAtIndex:row];
3786 // XXX: weird warning, stupid selectors ;P
3787 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3790 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3794 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3798 - (void) tableRowSelected:(NSNotification*)notification {
3799 UITable *table([list_ table]);
3800 int row([table selectedRow]);
3804 Source *source = [sources_ objectAtIndex:row];
3806 PackageTable *packages = [[[PackageTable alloc]
3809 title:[source label]
3810 filter:@selector(isVisibleInSource:)
3814 [packages setDelegate:delegate_];
3816 [book_ pushPage:packages];
3819 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3820 Source *source = [sources_ objectAtIndex:row];
3821 return [source record] != nil;
3824 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3825 [[list_ table] setDeleteConfirmationRow:row];
3828 - (void) table:(UITable *)table deleteRow:(int)row {
3829 Source *source = [sources_ objectAtIndex:row];
3830 [Sources_ removeObjectForKey:[source key]];
3831 [delegate_ syncData];
3834 - (void) _endConnection:(NSURLConnection *)connection {
3835 NSURLConnection **field = NULL;
3836 if (connection == trivial_bz2_)
3837 field = &trivial_bz2_;
3838 else if (connection == trivial_gz_)
3839 field = &trivial_gz_;
3840 _assert(field != NULL);
3841 [connection release];
3845 trivial_bz2_ == nil &&
3848 [delegate_ setStatusBarShowsProgress:NO];
3851 [hud_ removeFromSuperview];
3856 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3859 @"./", @"Distribution",
3860 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3862 [delegate_ syncData];
3863 } else if (error_ != nil) {
3864 UIActionSheet *sheet = [[[UIActionSheet alloc]
3865 initWithTitle:@"Verification Error"
3866 buttons:[NSArray arrayWithObjects:@"OK", nil]
3867 defaultButtonIndex:0
3872 [sheet setBodyText:[error_ localizedDescription]];
3873 [sheet popupAlertAnimated:YES];
3875 UIActionSheet *sheet = [[[UIActionSheet alloc]
3876 initWithTitle:@"Did not Find Repository"
3877 buttons:[NSArray arrayWithObjects:@"OK", nil]
3878 defaultButtonIndex:0
3883 [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."];
3884 [sheet popupAlertAnimated:YES];
3890 if (error_ != nil) {
3897 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3898 switch ([response statusCode]) {
3904 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3905 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3907 error_ = [error retain];
3908 [self _endConnection:connection];
3911 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3912 [self _endConnection:connection];
3915 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
3916 NSMutableURLRequest *request = [NSMutableURLRequest
3917 requestWithURL:[NSURL URLWithString:href]
3918 cachePolicy:NSURLRequestUseProtocolCachePolicy
3919 timeoutInterval:20.0
3922 [request setHTTPMethod:method];
3924 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
3927 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3928 NSString *context = [sheet context];
3929 if ([context isEqualToString:@"source"])
3932 NSString *href = [[sheet textField] text];
3934 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
3936 if (![href hasSuffix:@"/"])
3937 href_ = [href stringByAppendingString:@"/"];
3940 href_ = [href_ retain];
3942 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
3943 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
3944 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
3948 hud_ = [delegate_ addProgressHUD];
3949 [hud_ setText:@"Verifying URL"];
3962 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3963 if ((self = [super initWithBook:book]) != nil) {
3964 database_ = database;
3965 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
3967 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
3968 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3969 [list_ setShouldHideHeaderInShortLists:NO];
3971 [self addSubview:list_];
3972 [list_ setDataSource:self];
3974 UITableColumn *column = [[UITableColumn alloc]
3975 initWithTitle:@"Name"
3977 width:[self frame].size.width
3980 UITable *table = [list_ table];
3981 [table setSeparatorStyle:1];
3982 [table addTableColumn:column];
3983 [table setDelegate:self];
3989 - (void) reloadData {
3991 _assert(list.ReadMainList());
3993 [sources_ removeAllObjects];
3994 [sources_ addObjectsFromArray:[database_ sources]];
3995 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
3997 int count = [sources_ count];
3998 for (offset_ = 0; offset_ != count; ++offset_) {
3999 Source *source = [sources_ objectAtIndex:offset_];
4000 if ([source record] == nil)
4007 - (void) resetViewAnimated:(BOOL)animated {
4008 [list_ resetViewAnimated:animated];
4011 - (void) _leftButtonClicked {
4012 /*[book_ pushPage:[[[AddSourceView alloc]
4017 UIActionSheet *sheet = [[[UIActionSheet alloc]
4018 initWithTitle:@"Enter Cydia/APT URL"
4019 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4020 defaultButtonIndex:0
4025 [sheet addTextFieldWithValue:@"http://" label:@""];
4027 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4028 [traits setAutocapitalizationType:0];
4029 [traits setKeyboardType:3];
4030 [traits setAutocorrectionType:1];
4032 [sheet popupAlertAnimated:YES];
4035 - (void) _rightButtonClicked {
4036 UITable *table = [list_ table];
4037 BOOL editing = [table isRowDeletionEnabled];
4038 [table enableRowDeletion:!editing animated:YES];
4039 [book_ reloadButtonsForPage:self];
4042 - (NSString *) title {
4046 - (NSString *) backButtonTitle {
4050 - (NSString *) leftButtonTitle {
4051 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4054 - (NSString *) rightButtonTitle {
4055 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4058 - (RVUINavBarButtonStyle) rightButtonStyle {
4059 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4065 /* Installed View {{{ */
4066 @interface InstalledView : RVPage {
4067 _transient Database *database_;
4068 PackageTable *packages_;
4072 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4076 @implementation InstalledView
4079 [packages_ release];
4083 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4084 if ((self = [super initWithBook:book]) != nil) {
4085 database_ = database;
4087 packages_ = [[PackageTable alloc]
4091 filter:@selector(isInstalledAndVisible:)
4092 with:[NSNumber numberWithBool:YES]
4095 [self addSubview:packages_];
4099 - (void) resetViewAnimated:(BOOL)animated {
4100 [packages_ resetViewAnimated:animated];
4103 - (void) reloadData {
4104 [packages_ reloadData];
4107 - (void) _rightButtonClicked {
4108 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4109 [packages_ reloadData];
4111 [book_ reloadButtonsForPage:self];
4114 - (NSString *) title {
4115 return @"Installed";
4118 - (NSString *) backButtonTitle {
4122 - (NSString *) rightButtonTitle {
4123 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4126 - (RVUINavBarButtonStyle) rightButtonStyle {
4127 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4130 - (void) setDelegate:(id)delegate {
4131 [super setDelegate:delegate];
4132 [packages_ setDelegate:delegate];
4139 @interface HomeView : BrowserView {
4144 @implementation HomeView
4146 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4150 - (void) _leftButtonClicked {
4151 UIActionSheet *sheet = [[[UIActionSheet alloc]
4152 initWithTitle:@"About Cydia Installer"
4153 buttons:[NSArray arrayWithObjects:@"Close", nil]
4154 defaultButtonIndex:0
4160 @"Copyright (C) 2008\n"
4161 "Jay Freeman (saurik)\n"
4162 "saurik@saurik.com\n"
4163 "http://www.saurik.com/\n"
4166 "http://www.theokorigroup.com/\n"
4168 "College of Creative Studies,\n"
4169 "University of California,\n"
4171 "http://www.ccs.ucsb.edu/"
4174 [sheet popupAlertAnimated:YES];
4177 - (NSString *) leftButtonTitle {
4183 /* Manage View {{{ */
4184 @interface ManageView : BrowserView {
4189 @implementation ManageView
4191 - (NSString *) title {
4195 - (void) _leftButtonClicked {
4196 [delegate_ askForSettings];
4199 - (NSString *) leftButtonTitle {
4203 - (NSString *) _rightButtonTitle {
4210 /* Browser Implementation {{{ */
4211 @implementation BrowserView
4214 WebView *webview = [webview_ webView];
4215 [webview setFrameLoadDelegate:nil];
4216 [webview setResourceLoadDelegate:nil];
4217 [webview setUIDelegate:nil];
4219 [scroller_ setDelegate:nil];
4220 [webview_ setDelegate:nil];
4222 [scroller_ release];
4225 [indicator_ release];
4231 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4232 [self loadRequest:[NSURLRequest
4235 timeoutInterval:30.0
4239 - (void) loadURL:(NSURL *)url {
4240 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4243 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4244 NSMutableURLRequest *copy = [request mutableCopy];
4246 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4247 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4248 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4251 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4256 - (void) loadRequest:(NSURLRequest *)request {
4258 [webview_ loadRequest:request];
4261 - (void) reloadURL {
4262 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4263 [urls_ removeLastObject];
4264 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4267 - (WebView *) webView {
4268 return [webview_ webView];
4271 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4272 [scroller_ setContentSize:frame.size];
4275 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4276 [self view:sender didSetFrame:frame];
4279 - (void) pushPage:(RVPage *)page {
4280 [self setBackButtonTitle:title_];
4281 [page setDelegate:delegate_];
4282 [book_ pushPage:page];
4285 - (RVPage *) _pageForPackage:(NSString *)name {
4286 if (Package *package = [database_ packageWithName:name]) {
4287 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4288 [view setPackage:package];
4291 UIActionSheet *sheet = [[[UIActionSheet alloc]
4292 initWithTitle:@"Cannot Locate Package"
4293 buttons:[NSArray arrayWithObjects:@"Close", nil]
4294 defaultButtonIndex:0
4299 [sheet setBodyText:[NSString stringWithFormat:
4300 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4303 [sheet popupAlertAnimated:YES];
4308 - (BOOL) getSpecial:(NSString *)href {
4312 [href hasPrefix:@"http://phobos.apple.com/"] ||
4313 [href hasPrefix:@"mailto:"]
4315 [delegate_ openURL:[NSURL URLWithString:href]];
4316 else if ([href isEqualToString:@"cydia://add-source"])
4317 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4318 else if ([href isEqualToString:@"cydia://sources"])
4319 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4320 else if ([href isEqualToString:@"cydia://packages"])
4321 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4322 else if ([href hasPrefix:@"cydia://files/"]) {
4323 NSString *name = [href substringFromIndex:14];
4325 if (Package *package = [database_ packageWithName:name]) {
4326 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
4327 [files setPackage:package];
4330 } else if ([href hasPrefix:@"apptapp://package/"])
4331 page = [self _pageForPackage:[href substringFromIndex:18]];
4332 else if ([href hasPrefix:@"cydia://package/"])
4333 page = [self _pageForPackage:[href substringFromIndex:16]];
4334 else if (![href hasPrefix:@"apptapp:"] && ![href hasPrefix:@"cydia:"])
4338 [self pushPage:page];
4342 - (void) webView:(WebView *)sender willClickElement:(id)element {
4343 if ([[element localName] isEqualToString:@"img"])
4344 do if ((element = [element parentNode]) == nil)
4346 while (![[element localName] isEqualToString:@"a"]);
4347 if (![element respondsToSelector:@selector(href)])
4349 NSString *href = [element href];
4352 [self getSpecial:href];
4355 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4356 NSURL *url = [request URL];
4357 NSLog(@"Cydia:%@", url);
4358 if ([self getSpecial:[url absoluteString]])
4363 [book_ pushPage:self];
4366 return [self _addHeadersToRequest:request];
4369 - (BOOL) isSpecialScheme:(NSString *)scheme {
4371 [scheme isEqualToString:@"apptapp"] ||
4372 [scheme isEqualToString:@"cydia"] ||
4373 [scheme isEqualToString:@"mailto"];
4376 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4377 if (request != nil) {
4378 NSURL *url = [request URL];
4379 NSString *scheme = [url scheme];
4380 if ([self isSpecialScheme:scheme] || [[url absoluteString] hasPrefix:@"http://phobos.apple.com/"])
4384 [self setBackButtonTitle:title_];
4386 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4387 [browser setDelegate:delegate_];
4389 if (request != nil) {
4390 [browser loadRequest:[self _addHeadersToRequest:request]];
4391 [book_ pushPage:browser];
4394 return [browser webView];
4397 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4398 if ([frame parentFrame] != nil)
4401 title_ = [title retain];
4402 [self setTitle:title];
4405 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4406 if ([frame parentFrame] != nil)
4411 [indicator_ startAnimation];
4412 [self reloadButtons];
4414 if (title_ != nil) {
4419 [self setTitle:@"Loading..."];
4421 WebView *webview = [webview_ webView];
4422 NSString *href = [webview mainFrameURL];
4423 [urls_ addObject:[NSURL URLWithString:href]];
4425 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4427 CGRect webrect = [scroller_ bounds];
4428 webrect.size.height = 0;
4429 [webview_ setFrame:webrect];
4432 - (void) _finishLoading {
4435 [indicator_ stopAnimation];
4436 [self reloadButtons];
4440 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4442 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4445 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4446 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4449 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4450 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4453 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4454 return [webview_ webView:sender didCommitLoadForFrame:frame];
4457 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4458 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4461 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4462 if ([frame parentFrame] == nil)
4463 [self _finishLoading];
4464 return [webview_ webView:sender didFinishLoadForFrame:frame];
4467 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4468 if ([frame parentFrame] != nil)
4470 [self _finishLoading];
4472 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4473 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4474 [[error localizedDescription] stringByAddingPercentEscapes]
4478 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4479 if ((self = [super initWithBook:book]) != nil) {
4480 database_ = database;
4483 struct CGRect bounds = [self bounds];
4485 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4486 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4487 [self addSubview:pinstripe];
4489 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4490 [self addSubview:scroller_];
4492 [scroller_ setScrollingEnabled:YES];
4493 [scroller_ setAdjustForContentSizeChange:YES];
4494 [scroller_ setClipsSubviews:YES];
4495 [scroller_ setAllowsRubberBanding:YES];
4496 [scroller_ setScrollDecelerationFactor:0.99];
4497 [scroller_ setDelegate:self];
4499 CGRect webrect = [scroller_ bounds];
4500 webrect.size.height = 0;
4502 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4503 [scroller_ addSubview:webview_];
4505 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4507 [webview_ setTilingEnabled:YES];
4508 [webview_ setTileMinificationFilter:kCAFilterNearest];
4509 [webview_ setAutoresizes:YES];
4511 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4512 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4513 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4515 [webview_ _setDocumentType:0x4];
4517 [webview_ setZoomsFocusedFormControl:YES];
4518 [webview_ setContentsPosition:7];
4519 [webview_ setEnabledGestures:0xa];
4520 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4521 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4522 [webview_ setDelegate:self];
4523 [webview_ setGestureDelegate:self];
4524 [webview_ setSmoothsFonts:YES];
4526 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4527 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4528 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4530 Package *package([database_ packageWithName:@"cydia"]);
4531 NSString *application = package == nil ? @"Cydia" : [NSString
4532 stringWithFormat:@"Cydia/%@",
4536 WebView *webview = [webview_ webView];
4537 [webview setApplicationNameForUserAgent:application];
4538 [webview setFrameLoadDelegate:self];
4539 [webview setResourceLoadDelegate:self];
4540 [webview setUIDelegate:self];
4542 //[webview _setLayoutInterval:0.5];
4544 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4548 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4549 [webview_ redrawScaledDocument];
4552 - (void) _rightButtonClicked {
4557 - (NSString *) _rightButtonTitle {
4561 - (NSString *) rightButtonTitle {
4562 return loading_ ? @"" : [self _rightButtonTitle];
4565 - (NSString *) title {
4569 - (NSString *) backButtonTitle {
4573 - (void) setPageActive:(BOOL)active {
4575 [book_ addSubview:indicator_];
4577 [indicator_ removeFromSuperview];
4580 - (void) resetViewAnimated:(BOOL)animated {
4583 - (void) setPushed:(bool)pushed {
4590 @interface CYBook : RVBook <
4593 _transient Database *database_;
4595 UIProgressIndicator *indicator_;
4596 UITextLabel *prompt_;
4597 UIProgressBar *progress_;
4601 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4607 /* Install View {{{ */
4608 @interface InstallView : RVPage {
4609 _transient Database *database_;
4610 NSMutableArray *sections_;
4611 NSMutableArray *filtered_;
4612 UITransitionView *transition_;
4618 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4619 - (void) reloadData;
4624 @implementation InstallView
4627 [list_ setDataSource:nil];
4628 [list_ setDelegate:nil];
4630 [sections_ release];
4631 [filtered_ release];
4632 [transition_ release];
4634 [accessory_ release];
4638 - (int) numberOfRowsInTable:(UITable *)table {
4639 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4642 - (float) table:(UITable *)table heightForRow:(int)row {
4646 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4648 reusing = [[[SectionCell alloc] init] autorelease];
4649 [(SectionCell *)reusing setSection:(editing_ ?
4650 [sections_ objectAtIndex:row] :
4651 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4652 ) editing:editing_];
4656 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4660 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4664 - (void) tableRowSelected:(NSNotification *)notification {
4665 int row = [[notification object] selectedRow];
4676 title = @"All Packages";
4678 section = [filtered_ objectAtIndex:(row - 1)];
4679 name = [section name];
4685 title = @"(No Section)";
4689 PackageTable *table = [[[PackageTable alloc]
4693 filter:@selector(isVisiblyUninstalledInSection:)
4697 [table setDelegate:delegate_];
4699 [book_ pushPage:table];
4702 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4703 if ((self = [super initWithBook:book]) != nil) {
4704 database_ = database;
4706 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4707 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4709 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4710 [self addSubview:transition_];
4712 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4713 [transition_ transition:0 toView:list_];
4715 UITableColumn *column = [[[UITableColumn alloc]
4716 initWithTitle:@"Name"
4718 width:[self frame].size.width
4721 [list_ setDataSource:self];
4722 [list_ setSeparatorStyle:1];
4723 [list_ addTableColumn:column];
4724 [list_ setDelegate:self];
4725 [list_ setReusesTableCells:YES];
4731 - (void) reloadData {
4732 NSArray *packages = [database_ packages];
4734 [sections_ removeAllObjects];
4735 [filtered_ removeAllObjects];
4737 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4738 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4740 for (size_t i(0); i != [packages count]; ++i) {
4741 Package *package([packages objectAtIndex:i]);
4742 NSString *name([package section]);
4745 Section *section([sections objectForKey:name]);
4746 if (section == nil) {
4747 section = [[[Section alloc] initWithName:name] autorelease];
4748 [sections setObject:section forKey:name];
4752 if ([package valid] && [package installed] == nil && [package visible])
4753 [filtered addObject:package];
4756 [sections_ addObjectsFromArray:[sections allValues]];
4757 [sections_ sortUsingSelector:@selector(compareByName:)];
4759 [filtered sortUsingSelector:@selector(compareBySection:)];
4761 Section *section = nil;
4762 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4763 Package *package = [filtered objectAtIndex:offset];
4764 NSString *name = [package section];
4766 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4767 section = name == nil ?
4768 [[[Section alloc] initWithName:nil] autorelease] :
4769 [sections objectForKey:name];
4770 [filtered_ addObject:section];
4773 [section addToCount];
4779 - (void) resetView {
4781 [self _rightButtonClicked];
4784 - (void) resetViewAnimated:(BOOL)animated {
4785 [list_ resetViewAnimated:animated];
4788 - (void) _rightButtonClicked {
4789 if ((editing_ = !editing_))
4792 [delegate_ updateData];
4795 [book_ setTitle:[self title] forPage:self];
4796 [book_ reloadButtonsForPage:self];
4799 - (NSString *) title {
4800 return editing_ ? @"Section Visibility" : @"Install by Section";
4803 - (NSString *) backButtonTitle {
4807 - (NSString *) rightButtonTitle {
4808 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4811 - (UIView *) accessoryView {
4817 /* Changes View {{{ */
4818 @interface ChangesView : RVPage {
4819 _transient Database *database_;
4820 NSMutableArray *packages_;
4821 NSMutableArray *sections_;
4822 UISectionList *list_;
4826 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4827 - (void) reloadData;
4831 @implementation ChangesView
4834 [[list_ table] setDelegate:nil];
4835 [list_ setDataSource:nil];
4837 [packages_ release];
4838 [sections_ release];
4843 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4844 return [sections_ count];
4847 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4848 return [[sections_ objectAtIndex:section] name];
4851 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4852 return [[sections_ objectAtIndex:section] row];
4855 - (int) numberOfRowsInTable:(UITable *)table {
4856 return [packages_ count];
4859 - (float) table:(UITable *)table heightForRow:(int)row {
4860 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4863 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4865 reusing = [[[PackageCell alloc] init] autorelease];
4866 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4870 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4874 - (void) tableRowSelected:(NSNotification *)notification {
4875 int row = [[notification object] selectedRow];
4878 Package *package = [packages_ objectAtIndex:row];
4879 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4880 [view setDelegate:delegate_];
4881 [view setPackage:package];
4882 [book_ pushPage:view];
4885 - (void) _leftButtonClicked {
4886 [(CYBook *)book_ update];
4887 [self reloadButtons];
4890 - (void) _rightButtonClicked {
4891 [delegate_ distUpgrade];
4894 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4895 if ((self = [super initWithBook:book]) != nil) {
4896 database_ = database;
4898 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4899 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4901 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4902 [self addSubview:list_];
4904 [list_ setShouldHideHeaderInShortLists:NO];
4905 [list_ setDataSource:self];
4906 //[list_ setSectionListStyle:1];
4908 UITableColumn *column = [[[UITableColumn alloc]
4909 initWithTitle:@"Name"
4911 width:[self frame].size.width
4914 UITable *table = [list_ table];
4915 [table setSeparatorStyle:1];
4916 [table addTableColumn:column];
4917 [table setDelegate:self];
4918 [table setReusesTableCells:YES];
4924 - (void) reloadData {
4925 NSArray *packages = [database_ packages];
4927 [packages_ removeAllObjects];
4928 [sections_ removeAllObjects];
4930 for (size_t i(0); i != [packages count]; ++i) {
4931 Package *package([packages objectAtIndex:i]);
4934 [package installed] == nil && [package valid] && [package visible] ||
4935 [package upgradableAndEssential:NO]
4937 [packages_ addObject:package];
4940 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4942 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4943 Section *section = nil;
4946 bool unseens = false;
4948 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4950 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4951 Package *package = [packages_ objectAtIndex:offset];
4953 if ([package upgradableAndEssential:YES]) {
4955 [upgradable addToCount];
4958 NSDate *seen = [package seen];
4963 name = [@"n/a ?" retain];
4965 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4968 if (section == nil || ![[section name] isEqualToString:name]) {
4969 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4970 [sections_ addObject:section];
4974 [section addToCount];
4978 CFRelease(formatter);
4981 Section *last = [sections_ lastObject];
4982 size_t count = [last count];
4983 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4984 [sections_ removeLastObject];
4988 [sections_ insertObject:upgradable atIndex:0];
4991 [self reloadButtons];
4994 - (void) resetViewAnimated:(BOOL)animated {
4995 [list_ resetViewAnimated:animated];
4998 - (NSString *) leftButtonTitle {
4999 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5002 - (NSString *) rightButtonTitle {
5003 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5006 - (NSString *) title {
5012 /* Search View {{{ */
5013 @protocol SearchViewDelegate
5014 - (void) showKeyboard:(BOOL)show;
5017 @interface SearchView : RVPage {
5019 UISearchField *field_;
5020 UITransitionView *transition_;
5021 PackageTable *table_;
5022 UIPreferencesTable *advanced_;
5028 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5029 - (void) reloadData;
5033 @implementation SearchView
5037 [[field_ textTraits] setEditingDelegate:nil];
5039 [field_ setDelegate:nil];
5041 [accessory_ release];
5043 [transition_ release];
5045 [advanced_ release];
5050 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5054 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5056 case 0: return @"Advanced Search (Coming Soon!)";
5058 default: _assert(false);
5062 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5066 default: _assert(false);
5070 - (void) _showKeyboard:(BOOL)show {
5071 CGSize keysize = [UIKeyboard defaultSize];
5072 CGRect keydown = [book_ pageBounds];
5073 CGRect keyup = keydown;
5074 keyup.size.height -= keysize.height - ButtonBarHeight_;
5076 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5078 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5079 [animation setSignificantRectFields:8];
5082 [animation setStartFrame:keydown];
5083 [animation setEndFrame:keyup];
5085 [animation setStartFrame:keyup];
5086 [animation setEndFrame:keydown];
5089 UIAnimator *animator = [UIAnimator sharedAnimator];
5092 addAnimations:[NSArray arrayWithObjects:animation, nil]
5093 withDuration:(KeyboardTime_ - delay)
5098 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5101 [delegate_ showKeyboard:show];
5105 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5106 [self _showKeyboard:YES];
5109 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5110 [self _showKeyboard:NO];
5113 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5115 NSString *text([field_ text]);
5116 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5122 - (void) textFieldClearButtonPressed:(UITextField *)field {
5126 - (void) keyboardInputShouldDelete:(id)input {
5130 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5131 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5135 [field_ resignFirstResponder];
5140 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5141 if ((self = [super initWithBook:book]) != nil) {
5142 CGRect pageBounds = [book_ pageBounds];
5144 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5145 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5146 [self addSubview:pinstripe];*/
5148 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5149 [self addSubview:transition_];
5151 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5153 [advanced_ setReusesTableCells:YES];
5154 [advanced_ setDataSource:self];
5155 [advanced_ reloadData];
5157 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5158 CGColor dimmed(space_, 0, 0, 0, 0.5);
5159 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5161 table_ = [[PackageTable alloc]
5165 filter:@selector(isUnfilteredAndSearchedForBy:)
5169 [table_ setShouldHideHeaderInShortLists:NO];
5170 [transition_ transition:0 toView:table_];
5179 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5186 [self bounds].size.width - area.origin.x - 18;
5188 area.size.height = [UISearchField defaultHeight];
5190 field_ = [[UISearchField alloc] initWithFrame:area];
5192 UIFont *font = [UIFont systemFontOfSize:16];
5193 [field_ setFont:font];
5195 [field_ setPlaceholder:@"Package Names & Descriptions"];
5196 [field_ setDelegate:self];
5198 [field_ setPaddingTop:3];
5200 UITextInputTraits *traits = [field_ textInputTraits];
5201 [traits setAutocapitalizationType:0];
5202 [traits setAutocorrectionType:1];
5203 [traits setReturnKeyType:6];
5205 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5207 accessory_ = [[UIView alloc] initWithFrame:accrect];
5208 [accessory_ addSubview:field_];
5210 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5211 [configure setShowPressFeedback:YES];
5212 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5213 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5214 [accessory_ addSubview:configure];*/
5220 LKAnimation *animation = [LKTransition animation];
5221 [animation setType:@"oglFlip"];
5222 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5223 [animation setFillMode:@"extended"];
5224 [animation setTransitionFlags:3];
5225 [animation setDuration:10];
5226 [animation setSpeed:0.35];
5227 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5228 [[transition_ _layer] addAnimation:animation forKey:0];
5229 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5230 flipped_ = !flipped_;
5234 - (void) configurePushed {
5235 [field_ resignFirstResponder];
5239 - (void) resetViewAnimated:(BOOL)animated {
5242 [table_ resetViewAnimated:animated];
5245 - (void) reloadData {
5248 [table_ setObject:[field_ text]];
5249 [table_ reloadData];
5250 [table_ resetCursor];
5253 - (UIView *) accessoryView {
5257 - (NSString *) title {
5261 - (NSString *) backButtonTitle {
5265 - (void) setDelegate:(id)delegate {
5266 [table_ setDelegate:delegate];
5267 [super setDelegate:delegate];
5273 @implementation CYBook
5277 [indicator_ release];
5279 [progress_ release];
5283 - (NSString *) getTitleForPage:(RVPage *)page {
5284 return Simplify([super getTitleForPage:page]);
5292 [navbar_ setPrompt:@""];
5293 [navbar_ addSubview:overlay_];
5294 [indicator_ startAnimation];
5295 [prompt_ setText:@"Updating Database..."];
5296 [progress_ setProgress:0];
5301 detachNewThreadSelector:@selector(_update)
5310 [overlay_ removeFromSuperview];
5311 [indicator_ stopAnimation];
5312 [delegate_ reloadData];
5314 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5317 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5318 if ((self = [super initWithFrame:frame]) != nil) {
5319 database_ = database;
5321 CGRect ovrrect = [navbar_ bounds];
5322 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5324 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5326 bool ugly = [navbar_ _barStyle:NO] == 0;
5328 UIProgressIndicatorStyle style = ugly ?
5329 kUIProgressIndicatorStyleSmallBlack :
5330 kUIProgressIndicatorStyleSmallWhite;
5332 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5333 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5334 CGRect indrect = {{indoffset, indoffset}, indsize};
5336 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5337 [indicator_ setStyle:style];
5338 [overlay_ addSubview:indicator_];
5340 CGSize prmsize = {200, indsize.width + 4};
5343 indoffset * 2 + indsize.width,
5347 (ovrrect.size.height - prmsize.height) / 2
5350 UIFont *font = [UIFont systemFontOfSize:12];
5352 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5354 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : White_)]];
5355 [prompt_ setBackgroundColor:[UIColor colorWithCGColor:Clear_]];
5356 [prompt_ setFont:font];
5358 [overlay_ addSubview:prompt_];
5360 CGSize prgsize = {75, 100};
5363 ovrrect.size.width - prgsize.width - 10,
5364 (ovrrect.size.height - prgsize.height) / 2
5367 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5368 [progress_ setStyle:0];
5369 [overlay_ addSubview:progress_];
5374 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5377 status.setDelegate(self);
5379 [database_ updateWithStatus:status];
5382 performSelectorOnMainThread:@selector(_update_)
5390 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5391 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5394 - (void) setProgressTitle:(NSString *)title {
5396 performSelectorOnMainThread:@selector(_setProgressTitle:)
5402 - (void) setProgressPercent:(float)percent {
5405 - (void) addProgressOutput:(NSString *)output {
5407 performSelectorOnMainThread:@selector(_addProgressOutput:)
5413 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5417 - (void) _setProgressTitle:(NSString *)title {
5418 [prompt_ setText:[title stringByAppendingString:@"..."]];
5421 - (void) _addProgressOutput:(NSString *)output {
5426 @interface Cydia : UIApplication <
5427 ConfirmationViewDelegate,
5428 ProgressViewDelegate,
5437 UIToolbar *buttonbar_;
5439 ConfirmationView *confirm_;
5441 NSMutableArray *essential_;
5442 NSMutableArray *broken_;
5444 Database *database_;
5445 ProgressView *progress_;
5449 UIKeyboard *keyboard_;
5450 UIProgressHUD *hud_;
5452 InstallView *install_;
5453 ChangesView *changes_;
5454 ManageView *manage_;
5455 SearchView *search_;
5460 @implementation Cydia
5463 if ([broken_ count] != 0) {
5464 int count = [broken_ count];
5466 UIActionSheet *sheet = [[[UIActionSheet alloc]
5467 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5468 buttons:[NSArray arrayWithObjects:
5470 @"Ignore (Temporary)",
5472 defaultButtonIndex:0
5477 [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."];
5478 [sheet popupAlertAnimated:YES];
5479 } else if (!Ignored_ && [essential_ count] != 0) {
5480 int count = [essential_ count];
5482 UIActionSheet *sheet = [[[UIActionSheet alloc]
5483 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5484 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5485 defaultButtonIndex:0
5490 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5491 [sheet popupAlertAnimated:YES];
5495 - (void) _reloadData {
5496 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5497 [hud setText:@"Reloading Data"];
5498 [overlay_ addSubview:hud];
5501 [database_ reloadData];
5505 [essential_ removeAllObjects];
5506 [broken_ removeAllObjects];
5508 NSArray *packages = [database_ packages];
5509 for (int i(0), e([packages count]); i != e; ++i) {
5510 Package *package = [packages objectAtIndex:i];
5512 [broken_ addObject:package];
5513 if ([package upgradableAndEssential:NO]) {
5514 if ([package essential])
5515 [essential_ addObject:package];
5521 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5522 [buttonbar_ setBadgeValue:badge forButton:3];
5523 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5524 [buttonbar_ setBadgeAnimated:YES forButton:3];
5525 [self setApplicationBadge:badge];
5527 [buttonbar_ setBadgeValue:nil forButton:3];
5528 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5529 [buttonbar_ setBadgeAnimated:NO forButton:3];
5530 [self removeApplicationBadge];
5535 if ([packages count] == 0);
5544 [hud removeFromSuperview];*/
5547 - (void) _saveConfig {
5549 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5554 - (void) updateData {
5557 /* XXX: this is just stupid */
5559 [install_ reloadData];
5561 [changes_ reloadData];
5563 [search_ reloadData];
5573 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5574 _assert(file != NULL);
5576 NSArray *keys = [Sources_ allKeys];
5578 for (int i(0), e([keys count]); i != e; ++i) {
5579 NSString *key = [keys objectAtIndex:i];
5580 NSDictionary *source = [Sources_ objectForKey:key];
5582 fprintf(file, "%s %s %s\n",
5583 [[source objectForKey:@"Type"] UTF8String],
5584 [[source objectForKey:@"URI"] UTF8String],
5585 [[source objectForKey:@"Distribution"] UTF8String]
5594 detachNewThreadSelector:@selector(update_)
5597 title:@"Updating Sources..."
5601 - (void) reloadData {
5602 @synchronized (self) {
5603 if (confirm_ == nil)
5609 pkgProblemResolver *resolver = [database_ resolver];
5611 resolver->InstallProtect();
5612 if (!resolver->Resolve(true))
5617 [database_ prepare];
5619 if ([database_ cache]->BrokenCount() == 0)
5620 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5622 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5623 NSArray *packages = [database_ packages];
5625 for (size_t i(0); i != [packages count]; ++i) {
5626 Package *package = [packages objectAtIndex:i];
5627 if ([package broken])
5628 [broken addObject:[package name]];
5631 UIActionSheet *sheet = [[[UIActionSheet alloc]
5632 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5633 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5634 defaultButtonIndex:0
5639 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5640 [sheet popupAlertAnimated:YES];
5646 - (void) installPackage:(Package *)package {
5647 @synchronized (self) {
5654 - (void) removePackage:(Package *)package {
5655 @synchronized (self) {
5662 - (void) distUpgrade {
5663 @synchronized (self) {
5664 [database_ upgrade];
5670 @synchronized (self) {
5678 [overlay_ removeFromSuperview];
5682 detachNewThreadSelector:@selector(perform)
5689 - (void) bootstrap_ {
5691 [database_ upgrade];
5692 [database_ prepare];
5693 [database_ perform];
5696 - (void) bootstrap {
5698 detachNewThreadSelector:@selector(bootstrap_)
5701 title:@"Bootstrap Install..."
5705 - (void) progressViewIsComplete:(ProgressView *)progress {
5706 @synchronized (self) {
5709 if (confirm_ != nil) {
5710 [underlay_ addSubview:overlay_];
5711 [confirm_ removeFromSuperview];
5718 - (void) setPage:(RVPage *)page {
5719 [page resetViewAnimated:NO];
5720 [page setDelegate:self];
5721 [book_ setPage:page];
5724 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5725 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5726 [browser loadURL:url];
5730 - (void) _setHomePage {
5731 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5734 - (void) buttonBarItemTapped:(id)sender {
5735 unsigned tag = [sender tag];
5737 [book_ resetViewAnimated:YES];
5739 } else if (tag_ == 2 && tag != 2)
5740 [install_ resetView];
5743 case 1: [self _setHomePage]; break;
5745 case 2: [self setPage:install_]; break;
5746 case 3: [self setPage:changes_]; break;
5747 case 4: [self setPage:manage_]; break;
5748 case 5: [self setPage:search_]; break;
5750 default: _assert(false);
5756 - (void) fixSpringBoard {
5757 pid_t pid = ExecFork();
5761 if (pid_t child = fork()) {
5762 waitpid(child, NULL, 0);
5764 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5765 perror("launchctl unload");
5769 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5770 perror("launchctl load");
5775 - (void) applicationWillSuspend {
5780 [self fixSpringBoard];
5784 [super applicationWillSuspend];
5787 - (void) askForSettings {
5788 UIActionSheet *role = [[[UIActionSheet alloc]
5789 initWithTitle:@"Who Are You?"
5790 buttons:[NSArray arrayWithObjects:
5791 @"User (Graphical Only)",
5792 @"Hacker (+ Command Line)",
5793 @"Developer (No Filters)",
5795 defaultButtonIndex:-1
5800 [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."];
5801 [role popupAlertAnimated:YES];
5806 [self setStatusBarShowsProgress:NO];
5809 [hud_ removeFromSuperview];
5813 pid_t pid = ExecFork();
5815 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5816 perror("launchctl stop");
5823 [self askForSettings];
5827 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5829 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5830 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5831 0, 0, screenrect.size.width, screenrect.size.height - 48
5832 ) database:database_];
5834 [book_ setDelegate:self];
5836 [overlay_ addSubview:book_];
5838 NSArray *buttonitems = [NSArray arrayWithObjects:
5839 [NSDictionary dictionaryWithObjectsAndKeys:
5840 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5841 @"home-up.png", kUIButtonBarButtonInfo,
5842 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5843 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5844 self, kUIButtonBarButtonTarget,
5845 @"Home", kUIButtonBarButtonTitle,
5846 @"0", kUIButtonBarButtonType,
5849 [NSDictionary dictionaryWithObjectsAndKeys:
5850 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5851 @"install-up.png", kUIButtonBarButtonInfo,
5852 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5853 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5854 self, kUIButtonBarButtonTarget,
5855 @"Sections", kUIButtonBarButtonTitle,
5856 @"0", kUIButtonBarButtonType,
5859 [NSDictionary dictionaryWithObjectsAndKeys:
5860 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5861 @"changes-up.png", kUIButtonBarButtonInfo,
5862 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5863 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5864 self, kUIButtonBarButtonTarget,
5865 @"Changes", kUIButtonBarButtonTitle,
5866 @"0", kUIButtonBarButtonType,
5869 [NSDictionary dictionaryWithObjectsAndKeys:
5870 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5871 @"manage-up.png", kUIButtonBarButtonInfo,
5872 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5873 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5874 self, kUIButtonBarButtonTarget,
5875 @"Manage", kUIButtonBarButtonTitle,
5876 @"0", kUIButtonBarButtonType,
5879 [NSDictionary dictionaryWithObjectsAndKeys:
5880 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5881 @"search-up.png", kUIButtonBarButtonInfo,
5882 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5883 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5884 self, kUIButtonBarButtonTarget,
5885 @"Search", kUIButtonBarButtonTitle,
5886 @"0", kUIButtonBarButtonType,
5890 buttonbar_ = [[UIToolbar alloc]
5892 withFrame:CGRectMake(
5893 0, screenrect.size.height - ButtonBarHeight_,
5894 screenrect.size.width, ButtonBarHeight_
5896 withItemList:buttonitems
5899 [buttonbar_ setDelegate:self];
5900 [buttonbar_ setBarStyle:1];
5901 [buttonbar_ setButtonBarTrackingMode:2];
5903 int buttons[5] = {1, 2, 3, 4, 5};
5904 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5905 [buttonbar_ showButtonGroup:0 withDuration:0];
5907 for (int i = 0; i != 5; ++i)
5908 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5909 i * 64 + 2, 1, 60, ButtonBarHeight_
5912 [buttonbar_ showSelectionForButton:1];
5913 [overlay_ addSubview:buttonbar_];
5915 [UIKeyboard initImplementationNow];
5916 CGSize keysize = [UIKeyboard defaultSize];
5917 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5918 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5919 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5920 [overlay_ addSubview:keyboard_];
5922 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5923 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5924 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5926 manage_ = (ManageView *) [[self
5927 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5928 withClass:[ManageView class]
5932 [underlay_ addSubview:overlay_];
5939 [self _setHomePage];
5942 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5943 NSString *context = [sheet context];
5944 if ([context isEqualToString:@"fixhalf"])
5947 @synchronized (self) {
5948 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5949 Package *broken = [broken_ objectAtIndex:i];
5952 NSString *id = [broken id];
5953 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5954 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5955 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5956 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5965 [broken_ removeAllObjects];
5972 else if ([context isEqualToString:@"role"]) {
5974 case 1: Role_ = @"User"; break;
5975 case 2: Role_ = @"Hacker"; break;
5976 case 3: Role_ = @"Developer"; break;
5983 bool reset = Settings_ != nil;
5985 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
5989 [Metadata_ setObject:Settings_ forKey:@"Settings"];
5997 } else if ([context isEqualToString:@"upgrade"])
6000 @synchronized (self) {
6001 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6002 Package *essential = [essential_ objectAtIndex:i];
6003 [essential install];
6022 - (void) reorganize {
6023 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6024 system("/usr/libexec/cydia/free.sh");
6025 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6029 - (void) applicationSuspend:(__GSEvent *)event {
6030 if (hud_ == nil && ![progress_ isRunning])
6031 [super applicationSuspend:event];
6034 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6036 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6039 - (void) _setSuspended:(BOOL)value {
6041 [super _setSuspended:value];
6044 - (UIProgressHUD *) addProgressHUD {
6045 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6047 [underlay_ addSubview:hud];
6051 - (void) applicationDidFinishLaunching:(id)unused {
6052 Font12_ = [[UIFont systemFontOfSize:12] retain];
6053 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6054 Font14_ = [[UIFont systemFontOfSize:14] retain];
6055 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6056 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6058 _assert(pkgInitConfig(*_config));
6059 _assert(pkgInitSystem(*_config, _system));
6064 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6065 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6067 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6068 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6070 [window_ orderFront:self];
6071 [window_ makeKey:self];
6072 [window_ _setHidden:NO];
6074 database_ = [[Database alloc] init];
6075 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6076 [database_ setDelegate:progress_];
6077 [window_ setContentView:progress_];
6079 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6080 [progress_ setContentView:underlay_];
6082 [progress_ resetView];
6085 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6086 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6087 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6088 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6089 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6090 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6091 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6093 [self setIdleTimerDisabled:YES];
6095 hud_ = [self addProgressHUD];
6096 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6098 [self setStatusBarShowsProgress:YES];
6101 detachNewThreadSelector:@selector(reorganize)
6109 - (void) showKeyboard:(BOOL)show {
6110 CGSize keysize = [UIKeyboard defaultSize];
6111 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6112 CGRect keyup = keydown;
6113 keyup.origin.y -= keysize.height;
6115 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6116 [animation setSignificantRectFields:2];
6119 [animation setStartFrame:keydown];
6120 [animation setEndFrame:keyup];
6121 [keyboard_ activate];
6123 [animation setStartFrame:keyup];
6124 [animation setEndFrame:keydown];
6125 [keyboard_ deactivate];
6128 [[UIAnimator sharedAnimator]
6129 addAnimations:[NSArray arrayWithObjects:animation, nil]
6130 withDuration:KeyboardTime_
6135 - (void) slideUp:(UIActionSheet *)alert {
6137 [alert presentSheetFromButtonBar:buttonbar_];
6139 [alert presentSheetInView:overlay_];
6144 void AddPreferences(NSString *plist) {
6145 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6147 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6148 _assert(settings != NULL);
6149 NSMutableArray *items = [settings objectForKey:@"items"];
6153 for (size_t i(0); i != [items count]; ++i) {
6154 NSMutableDictionary *item([items objectAtIndex:i]);
6155 NSString *label = [item objectForKey:@"label"];
6156 if (label != nil && [label isEqualToString:@"Cydia"]) {
6163 for (size_t i(0); i != [items count]; ++i) {
6164 NSDictionary *item([items objectAtIndex:i]);
6165 NSString *label = [item objectForKey:@"label"];
6166 if (label != nil && [label isEqualToString:@"General"]) {
6167 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6168 @"CydiaSettings", @"bundle",
6169 @"PSLinkCell", @"cell",
6170 [NSNumber numberWithBool:YES], @"hasIcon",
6171 [NSNumber numberWithBool:YES], @"isController",
6173 nil] atIndex:(i + 1)];
6179 _assert([settings writeToFile:plist atomically:YES] == YES);
6186 id Alloc_(id self, SEL selector) {
6187 id object = alloc_(self, selector);
6188 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6193 id Dealloc_(id self, SEL selector) {
6194 id object = dealloc_(self, selector);
6195 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6199 int main(int argc, char *argv[]) {
6200 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6202 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6204 App_ = [[NSBundle mainBundle] bundlePath];
6205 Home_ = NSHomeDirectory();
6208 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6209 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6210 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6211 Sounds_Keyboard_ = [keyboard boolValue];
6217 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6218 _assert(errno == ENOENT);
6219 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6220 _assert(errno == ENOENT);
6222 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6223 alloc_ = alloc->method_imp;
6224 alloc->method_imp = (IMP) &Alloc_;*/
6226 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6227 dealloc_ = dealloc->method_imp;
6228 dealloc->method_imp = (IMP) &Dealloc_;*/
6230 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6231 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6232 Firmware_ = strdup([prover UTF8String]);
6233 NSArray *versions = [prover componentsSeparatedByString:@"."];
6234 int count = [versions count];
6235 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6236 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6237 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6242 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6243 char *machine = new char[size];
6244 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6247 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6248 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6249 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6250 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6254 IOObjectRelease(service);
6257 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6258 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6260 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6261 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6263 Settings_ = [Metadata_ objectForKey:@"Settings"];
6265 Packages_ = [Metadata_ objectForKey:@"Packages"];
6266 Sections_ = [Metadata_ objectForKey:@"Sections"];
6267 Sources_ = [Metadata_ objectForKey:@"Sources"];
6270 if (Settings_ != nil)
6271 Role_ = [Settings_ objectForKey:@"Role"];
6273 if (Packages_ == nil) {
6274 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6275 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6278 if (Sections_ == nil) {
6279 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6280 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6283 if (Sources_ == nil) {
6284 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6285 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6288 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6289 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6291 if (access("/User", F_OK) != 0)
6292 system("/usr/libexec/cydia/firmware.sh");
6294 Locale_ = CFLocaleCopyCurrent();
6295 space_ = CGColorSpaceCreateDeviceRGB();
6297 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6298 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6299 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6300 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6301 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6302 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6303 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6305 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6307 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6309 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6311 CGColorSpaceRelease(space_);