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.
39 #define UITextTraits UITextInputTraits
40 #define textTraits textInputTraits
41 #define setAutoCapsType setAutocapitalizationType
42 #define setAutoCorrectionType setAutocorrectionType
43 #define setPreferredKeyboardType setKeyboardType
46 /* #include Directives {{{ */
47 #include <objc/objc.h>
48 #include <objc/runtime.h>
50 #include <CoreGraphics/CoreGraphics.h>
51 #include <GraphicsServices/GraphicsServices.h>
52 #include <Foundation/Foundation.h>
53 #include <UIKit/UIKit.h>
54 #include <WebCore/DOMHTML.h>
56 #import "BrowserView.h"
58 #import "UICaboodle.h"
60 #include <WebKit/WebFrame.h>
61 #include <WebKit/WebView.h>
66 #include <ext/stdio_filebuf.h>
68 #include <apt-pkg/acquire.h>
69 #include <apt-pkg/acquire-item.h>
70 #include <apt-pkg/algorithms.h>
71 #include <apt-pkg/cachefile.h>
72 #include <apt-pkg/clean.h>
73 #include <apt-pkg/configuration.h>
74 #include <apt-pkg/debmetaindex.h>
75 #include <apt-pkg/error.h>
76 #include <apt-pkg/init.h>
77 #include <apt-pkg/pkgrecords.h>
78 #include <apt-pkg/sourcelist.h>
79 #include <apt-pkg/sptr.h>
81 #include <sys/sysctl.h>
85 #include <mach-o/nlist.h>
96 /* iPhoneOS 2.0 Compatibility {{{ */
98 @interface UICGColor : NSObject {
101 - (id) initWithCGColor:(CGColorRef)color;
107 + (id)systemFontOfSize:(float)fp8;
108 + (id)boldSystemFontOfSize:(float)fp8;
109 - (UIFont *) fontWithSize:(CGFloat)size;
112 @interface NSObject (iPhoneOS)
113 - (CGColorRef) cgColor;
114 - (CGColorRef) CGColor;
118 @implementation NSObject (iPhoneOS)
120 - (CGColorRef) cgColor {
121 return [self CGColor];
124 - (CGColorRef) CGColor {
125 return (CGColorRef) self;
129 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
134 @interface UITextView (iPhoneOS)
135 - (void) setTextSize:(float)size;
138 @implementation UITextView (iPhoneOS)
140 - (void) setTextSize:(float)size {
141 [self setFont:[[self font] fontWithSize:size]];
148 @interface UIApplication (IdleTimer)
149 - (void) setIdleTimerDisabled:(char)arg0;
154 kUIProgressIndicatorStyleMediumWhite = 1,
155 kUIProgressIndicatorStyleSmallWhite = 0,
156 kUIProgressIndicatorStyleSmallBlack = 4
157 } UIProgressIndicatorStyle;
160 kUIProgressIndicatorStyleMediumWhite = 0,
161 kUIProgressIndicatorStyleSmallWhite = 2,
162 kUIProgressIndicatorStyleSmallBlack = 3
163 } UIProgressIndicatorStyle;
167 kUIControlEventMouseDown = 1 << 0,
168 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
169 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
170 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
171 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
172 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
173 } UIControlEventMasks;
175 @interface NSString (UIKit)
176 - (NSString *) stringByAddingPercentEscapes;
177 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
180 @interface NSString (Cydia)
181 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
182 - (NSComparisonResult) compareByPath:(NSString *)other;
185 @implementation NSString (Cydia)
187 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
188 char data[length + 1];
189 memcpy(data, bytes, length);
191 return [NSString stringWithUTF8String:data];
194 - (NSComparisonResult) compareByPath:(NSString *)other {
195 NSString *prefix = [self commonPrefixWithString:other options:0];
196 size_t length = [prefix length];
198 NSRange lrange = NSMakeRange(length, [self length] - length);
199 NSRange rrange = NSMakeRange(length, [other length] - length);
201 lrange = [self rangeOfString:@"/" options:0 range:lrange];
202 rrange = [other rangeOfString:@"/" options:0 range:rrange];
204 NSComparisonResult value;
206 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
207 value = NSOrderedSame;
208 else if (lrange.location == NSNotFound)
209 value = NSOrderedAscending;
210 else if (rrange.location == NSNotFound)
211 value = NSOrderedDescending;
213 value = NSOrderedSame;
215 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
216 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
217 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
218 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
220 NSComparisonResult result = [lpath compare:rpath];
221 return result == NSOrderedSame ? value : result;
226 /* Perl-Compatible RegEx {{{ */
236 Pcre(const char *regex) :
241 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
244 fprintf(stderr, "%d:%s\n", offset, error);
248 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
249 matches_ = new int[(capture_ + 1) * 3];
257 NSString *operator [](size_t match) {
258 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
261 bool operator ()(NSString *data) {
262 // XXX: length is for characters, not for bytes
263 return operator ()([data UTF8String], [data length]);
266 bool operator ()(const char *data, size_t size) {
268 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
272 /* Mime Addresses {{{ */
273 @interface Address : NSObject {
279 - (NSString *) email;
281 + (Address *) addressWithString:(NSString *)string;
282 - (Address *) initWithString:(NSString *)string;
285 @implementation Address
294 - (NSString *) name {
298 - (NSString *) email {
302 + (Address *) addressWithString:(NSString *)string {
303 return [[[Address alloc] initWithString:string] autorelease];
306 - (Address *) initWithString:(NSString *)string {
307 if ((self = [super init]) != nil) {
308 const char *data = [string UTF8String];
309 size_t size = [string length];
311 static Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
313 if (email_r(data, size)) {
314 name_ = [email_r[1] retain];
315 email_ = [email_r[2] retain];
317 name_ = [[NSString alloc]
320 encoding:kCFStringEncodingUTF8
330 /* CoreGraphics Primitives {{{ */
341 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
344 Set(space, red, green, blue, alpha);
349 CGColorRelease(color_);
356 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
358 float color[] = {red, green, blue, alpha};
359 color_ = CGColorCreate(space, color);
362 operator CGColorRef() {
377 /* Right Alignment {{{ */
378 @interface UIRightTextLabel : UITextLabel {
379 float _savedRightEdgeX;
380 BOOL _sizedtofit_flag;
383 - (void) setFrame:(CGRect)frame;
384 - (void) setText:(NSString *)text;
385 - (void) realignText;
388 @implementation UIRightTextLabel
390 - (void) setFrame:(CGRect)frame {
391 [super setFrame:frame];
392 if (_sizedtofit_flag == NO) {
393 _savedRightEdgeX = frame.origin.x;
398 - (void) setText:(NSString *)text {
399 [super setText:text];
403 - (void) realignText {
404 CGRect oldFrame = [self frame];
406 _sizedtofit_flag = YES;
407 [self sizeToFit]; // shrink down size so I can right align it
409 CGRect newFrame = [self frame];
411 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
412 oldFrame.size.width = newFrame.size.width;
413 [super setFrame:oldFrame];
414 _sizedtofit_flag = NO;
420 extern "C" void UISetColor(CGColorRef color);
422 /* Random Global Variables {{{ */
423 static const int PulseInterval_ = 50000;
424 static const int ButtonBarHeight_ = 48;
425 static const float KeyboardTime_ = 0.3f;
426 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
432 static CGColor Blue_;
433 static CGColor Blueish_;
434 static CGColor Black_;
435 static CGColor Clear_;
437 static CGColor White_;
438 static CGColor Gray_;
440 static NSString *Home_;
441 static BOOL Sounds_Keyboard_;
443 static BOOL Advanced_;
444 //static BOOL Loaded_;
445 static BOOL Ignored_;
447 static UIFont *Font12_;
448 static UIFont *Font12Bold_;
449 static UIFont *Font14_;
450 static UIFont *Font18Bold_;
451 static UIFont *Font22Bold_;
453 const char *Firmware_ = NULL;
454 const char *Machine_ = NULL;
455 const char *SerialNumber_ = NULL;
462 CGColorSpaceRef space_;
464 #define FW_LEAST(major, minor, bugfix) \
465 (major < Major_ || major == Major_ && \
466 (minor < Minor_ || minor == Minor_ && \
472 static NSMutableDictionary *SectionMap_;
473 static NSMutableDictionary *Metadata_;
474 static _transient NSMutableDictionary *Settings_;
475 static _transient NSString *Role_;
476 static _transient NSMutableDictionary *Packages_;
477 static _transient NSMutableDictionary *Sections_;
478 static _transient NSMutableDictionary *Sources_;
479 static bool Changed_;
482 NSString *GetLastUpdate() {
483 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
486 return @"Never or Unknown";
488 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
489 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
491 CFRelease(formatter);
493 return [(NSString *) formatted autorelease];
496 /* Display Helpers {{{ */
497 inline float Interpolate(float begin, float end, float fraction) {
498 return (end - begin) * fraction + begin;
501 NSString *SizeString(double size) {
503 while (size > 1024) {
508 static const char *powers_[] = {"B", "kB", "MB", "GB"};
510 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
513 NSString *StripVersion(NSString *version) {
514 NSRange colon = [version rangeOfString:@":"];
515 if (colon.location != NSNotFound)
516 version = [version substringFromIndex:(colon.location + 1)];
520 static const float TextViewOffset_ = 22;
522 UITextView *GetTextView(NSString *value, float left, bool html) {
523 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
524 [text setEditable:NO];
525 [text setTextSize:16];
527 [text setHTML:value];
529 [text setText:value];
530 [text setEnabled:NO];
532 [text setBackgroundColor:Clear_];
534 CGRect frame = [text frame];
535 [text setFrame:frame];
536 CGRect rect = [text visibleTextRect];
537 frame.size.height = rect.size.height;
538 [text setFrame:frame];
543 NSString *Simplify(NSString *title) {
544 const char *data = [title UTF8String];
545 size_t size = [title length];
547 static Pcre square_r("^\\[(.*)\\]$");
548 if (square_r(data, size))
549 return Simplify(square_r[1]);
551 static Pcre paren_r("^\\((.*)\\)$");
552 if (paren_r(data, size))
553 return Simplify(paren_r[1]);
555 static Pcre title_r("^(.*?) \\(.*\\)$");
556 if (title_r(data, size))
557 return Simplify(title_r[1]);
563 bool isSectionVisible(NSString *section) {
564 NSDictionary *metadata = [Sections_ objectForKey:section];
565 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
566 return hidden == nil || ![hidden boolValue];
569 /* Delegate Prototypes {{{ */
573 @interface NSObject (ProgressDelegate)
576 @implementation NSObject(ProgressDelegate)
578 - (void) _setProgressError:(NSArray *)args {
579 [self performSelector:@selector(setProgressError:forPackage:)
580 withObject:[args objectAtIndex:0]
581 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
587 @protocol ProgressDelegate
588 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
589 - (void) setProgressTitle:(NSString *)title;
590 - (void) setProgressPercent:(float)percent;
591 - (void) addProgressOutput:(NSString *)output;
594 @protocol ConfigurationDelegate
595 - (void) repairWithSelector:(SEL)selector;
596 - (void) setConfigurationData:(NSString *)data;
599 @protocol CydiaDelegate
600 - (void) installPackage:(Package *)package;
601 - (void) removePackage:(Package *)package;
602 - (void) slideUp:(UIAlertSheet *)alert;
603 - (void) distUpgrade;
606 - (void) askForSettings;
607 - (UIProgressHUD *) addProgressHUD;
611 /* Status Delegation {{{ */
613 public pkgAcquireStatus
616 _transient NSObject<ProgressDelegate> *delegate_;
624 void setDelegate(id delegate) {
625 delegate_ = delegate;
628 virtual bool MediaChange(std::string media, std::string drive) {
632 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
635 virtual void Fetch(pkgAcquire::ItemDesc &item) {
636 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
639 virtual void Done(pkgAcquire::ItemDesc &item) {
642 virtual void Fail(pkgAcquire::ItemDesc &item) {
644 item.Owner->Status == pkgAcquire::Item::StatIdle ||
645 item.Owner->Status == pkgAcquire::Item::StatDone
649 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
650 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
655 virtual bool Pulse(pkgAcquire *Owner) {
656 bool value = pkgAcquireStatus::Pulse(Owner);
659 double(CurrentBytes + CurrentItems) /
660 double(TotalBytes + TotalItems)
663 [delegate_ setProgressPercent:percent];
667 virtual void Start() {
670 virtual void Stop() {
674 /* Progress Delegation {{{ */
679 _transient id<ProgressDelegate> delegate_;
682 virtual void Update() {
683 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
684 [delegate_ setProgressPercent:(Percent / 100)];
693 void setDelegate(id delegate) {
694 delegate_ = delegate;
697 virtual void Done() {
698 [delegate_ setProgressPercent:1];
703 /* Database Interface {{{ */
704 @interface Database : NSObject {
706 pkgDepCache::Policy *policy_;
707 pkgRecords *records_;
708 pkgProblemResolver *resolver_;
709 pkgAcquire *fetcher_;
711 SPtr<pkgPackageManager> manager_;
712 pkgSourceList *list_;
714 NSMutableDictionary *sources_;
715 NSMutableArray *packages_;
717 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
726 - (void) _readCydia:(NSNumber *)fd;
727 - (void) _readStatus:(NSNumber *)fd;
728 - (void) _readOutput:(NSNumber *)fd;
732 - (Package *) packageWithName:(NSString *)name;
735 - (pkgCacheFile &) cache;
736 - (pkgDepCache::Policy *) policy;
737 - (pkgRecords *) records;
738 - (pkgProblemResolver *) resolver;
739 - (pkgAcquire &) fetcher;
740 - (NSArray *) packages;
741 - (NSArray *) sources;
750 - (void) updateWithStatus:(Status &)status;
752 - (void) setDelegate:(id)delegate;
753 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
757 /* Source Class {{{ */
758 @interface Source : NSObject {
759 NSString *description_;
764 NSString *distribution_;
768 NSString *defaultIcon_;
770 NSDictionary *record_;
774 - (Source *) initWithMetaIndex:(metaIndex *)index;
776 - (NSComparisonResult) compareByNameAndType:(Source *)source;
778 - (NSDictionary *) record;
782 - (NSString *) distribution;
788 - (NSString *) description;
789 - (NSString *) label;
790 - (NSString *) origin;
791 - (NSString *) version;
793 - (NSString *) defaultIcon;
797 @implementation Source
801 [distribution_ release];
804 if (description_ != nil)
805 [description_ release];
812 if (defaultIcon_ != nil)
813 [defaultIcon_ release];
820 - (Source *) initWithMetaIndex:(metaIndex *)index {
821 if ((self = [super init]) != nil) {
822 trusted_ = index->IsTrusted();
824 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
825 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
826 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
834 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
835 if (dindex != NULL) {
836 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
838 while (std::getline(release, line)) {
839 std::string::size_type colon(line.find(':'));
840 if (colon == std::string::npos)
843 std::string name(line.substr(0, colon));
844 std::string value(line.substr(colon + 1));
845 while (!value.empty() && value[0] == ' ')
846 value = value.substr(1);
848 if (name == "Default-Icon")
849 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
850 else if (name == "Description")
851 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
852 else if (name == "Label")
853 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
854 else if (name == "Origin")
855 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
856 else if (name == "Version")
857 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
861 record_ = [Sources_ objectForKey:[self key]];
863 record_ = [record_ retain];
867 - (NSComparisonResult) compareByNameAndType:(Source *)source {
868 NSDictionary *lhr = [self record];
869 NSDictionary *rhr = [source record];
872 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
874 NSString *lhs = [self name];
875 NSString *rhs = [source name];
877 if ([lhs length] != 0 && [rhs length] != 0) {
878 unichar lhc = [lhs characterAtIndex:0];
879 unichar rhc = [rhs characterAtIndex:0];
881 if (isalpha(lhc) && !isalpha(rhc))
882 return NSOrderedAscending;
883 else if (!isalpha(lhc) && isalpha(rhc))
884 return NSOrderedDescending;
887 return [lhs caseInsensitiveCompare:rhs];
890 - (NSDictionary *) record {
902 - (NSString *) distribution {
903 return distribution_;
906 - (NSString *) type {
911 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
914 - (NSString *) host {
915 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
918 - (NSString *) name {
919 return origin_ == nil ? [self host] : origin_;
922 - (NSString *) description {
926 - (NSString *) label {
927 return label_ == nil ? [self host] : label_;
930 - (NSString *) origin {
934 - (NSString *) version {
938 - (NSString *) defaultIcon {
944 /* Relationship Class {{{ */
945 @interface Relationship : NSObject {
956 @implementation Relationship
964 - (NSString *) type {
972 - (NSString *) name {
979 /* Package Class {{{ */
980 NSString *Scour(const char *field, const char *begin, const char *end) {
981 size_t i(0), l(strlen(field));
984 const char *name = begin + i;
985 const char *colon = name + l;
986 const char *value = colon + 1;
991 memcmp(name, field, l) == 0
993 while (value != end && value[0] == ' ')
995 const char *line = std::find(value, end, '\n');
996 while (line != value && line[-1] == ' ')
999 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1001 begin = std::find(begin, end, '\n');
1009 @interface Package : NSObject {
1010 pkgCache::PkgIterator iterator_;
1011 _transient Database *database_;
1012 pkgCache::VerIterator version_;
1013 pkgCache::VerFileIterator file_;
1019 NSString *installed_;
1031 NSArray *relationships_;
1034 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1035 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1037 - (pkgCache::PkgIterator) iterator;
1039 - (NSString *) section;
1040 - (Address *) maintainer;
1042 - (NSString *) description;
1043 - (NSString *) index;
1047 - (NSString *) latest;
1048 - (NSString *) installed;
1051 - (BOOL) upgradableAndEssential:(BOOL)essential;
1057 - (BOOL) halfConfigured;
1058 - (BOOL) halfInstalled;
1060 - (NSString *) mode;
1063 - (NSString *) name;
1064 - (NSString *) tagline;
1065 - (NSString *) icon;
1066 - (NSString *) website;
1067 - (Address *) author;
1069 - (NSArray *) relationships;
1071 - (Source *) source;
1072 - (NSString *) role;
1074 - (BOOL) matches:(NSString *)text;
1076 - (bool) hasSupportingRole;
1077 - (BOOL) hasTag:(NSString *)tag;
1079 - (NSComparisonResult) compareByName:(Package *)package;
1080 - (NSComparisonResult) compareBySection:(Package *)package;
1081 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1082 - (NSComparisonResult) compareForChanges:(Package *)package;
1087 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search;
1088 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1089 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1090 - (NSNumber *) isVisibleInSource:(Source *)source;
1094 @implementation Package
1101 if (installed_ != nil)
1102 [installed_ release];
1110 if (website_ != nil)
1112 if (sponsor_ != nil)
1121 if (relationships_ != nil)
1122 [relationships_ release];
1127 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1128 if ((self = [super init]) != nil) {
1129 iterator_ = iterator;
1130 database_ = database;
1132 version_ = [database_ policy]->GetCandidateVer(iterator_);
1133 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1135 if (!version_.end())
1136 file_ = version_.FileList();
1138 pkgCache &cache([database_ cache]);
1139 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1142 pkgCache::VerIterator current = iterator_.CurrentVer();
1143 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1145 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1148 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1150 const char *begin, *end;
1151 parser->GetRec(begin, end);
1153 name_ = Scour("Name", begin, end);
1155 name_ = [name_ retain];
1156 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1157 icon_ = Scour("Icon", begin, end);
1159 icon_ = [icon_ retain];
1160 website_ = Scour("Homepage", begin, end);
1161 if (website_ == nil)
1162 website_ = Scour("Website", begin, end);
1163 if (website_ != nil)
1164 website_ = [website_ retain];
1165 NSString *sponsor = Scour("Sponsor", begin, end);
1167 sponsor_ = [[Address addressWithString:sponsor] retain];
1168 NSString *author = Scour("Author", begin, end);
1170 author_ = [[Address addressWithString:author] retain];
1171 NSString *tags = Scour("Tag", begin, end);
1173 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1177 for (int i(0), e([tags_ count]); i != e; ++i) {
1178 NSString *tag = [tags_ objectAtIndex:i];
1179 if ([tag hasPrefix:@"role::"]) {
1180 role_ = [[tag substringFromIndex:6] retain];
1185 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1186 if (metadata == nil || [metadata count] == 0) {
1187 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1191 [Packages_ setObject:metadata forKey:id_];
1197 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1198 return [[[Package alloc]
1199 initWithIterator:iterator
1204 - (pkgCache::PkgIterator) iterator {
1208 - (NSString *) section {
1209 const char *section = iterator_.Section();
1210 if (section == NULL)
1212 NSString *key = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1213 NSString *value = [SectionMap_ objectForKey:key];
1216 return [value stringByReplacingCharacter:'_' withCharacter:' '];
1219 - (Address *) maintainer {
1222 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1223 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1227 return version_.end() ? 0 : version_->InstalledSize;
1230 - (NSString *) description {
1233 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1234 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1236 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1237 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1238 if ([lines count] < 2)
1241 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1242 for (size_t i(1); i != [lines count]; ++i) {
1243 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1244 [trimmed addObject:trim];
1247 return [trimmed componentsJoinedByString:@"\n"];
1250 - (NSString *) index {
1251 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1252 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1256 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1259 - (NSString *) latest {
1263 - (NSString *) installed {
1268 return !version_.end();
1271 - (BOOL) upgradableAndEssential:(BOOL)essential {
1272 pkgCache::VerIterator current = iterator_.CurrentVer();
1275 return essential && [self essential];
1277 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1278 return !candidate.end() && candidate != current;
1282 - (BOOL) essential {
1283 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1287 return [database_ cache][iterator_].InstBroken();
1291 NSString *section = [self section];
1292 return [self hasSupportingRole] && (section == nil || isSectionVisible(section));
1296 unsigned char current = iterator_->CurrentState;
1297 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1300 - (BOOL) halfConfigured {
1301 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1304 - (BOOL) halfInstalled {
1305 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1309 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1310 return state.Mode != pkgDepCache::ModeKeep;
1313 - (NSString *) mode {
1314 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1316 switch (state.Mode) {
1317 case pkgDepCache::ModeDelete:
1318 if ((state.iFlags & pkgDepCache::Purge) != 0)
1323 case pkgDepCache::ModeKeep:
1324 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1329 case pkgDepCache::ModeInstall:
1330 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1331 return @"Reinstall";
1332 else switch (state.Status) {
1334 return @"Downgrade";
1340 return @"New Install";
1353 - (NSString *) name {
1354 return name_ == nil ? id_ : name_;
1357 - (NSString *) tagline {
1361 - (NSString *) icon {
1365 - (NSString *) website {
1369 - (Address *) sponsor {
1373 - (Address *) author {
1377 - (NSArray *) relationships {
1378 return relationships_;
1381 - (Source *) source {
1383 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1390 - (NSString *) role {
1394 - (BOOL) matches:(NSString *)text {
1400 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1401 if (range.location != NSNotFound)
1404 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1405 if (range.location != NSNotFound)
1408 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1409 if (range.location != NSNotFound)
1415 - (bool) hasSupportingRole {
1418 if ([role_ isEqualToString:@"enduser"])
1420 if ([Role_ isEqualToString:@"User"])
1422 if ([role_ isEqualToString:@"hacker"])
1424 if ([Role_ isEqualToString:@"Hacker"])
1426 if ([role_ isEqualToString:@"developer"])
1428 if ([Role_ isEqualToString:@"Developer"])
1433 - (BOOL) hasTag:(NSString *)tag {
1434 return tags_ == nil ? NO : [tags_ containsObject:tag];
1437 - (NSComparisonResult) compareByName:(Package *)package {
1438 NSString *lhs = [self name];
1439 NSString *rhs = [package name];
1441 if ([lhs length] != 0 && [rhs length] != 0) {
1442 unichar lhc = [lhs characterAtIndex:0];
1443 unichar rhc = [rhs characterAtIndex:0];
1445 if (isalpha(lhc) && !isalpha(rhc))
1446 return NSOrderedAscending;
1447 else if (!isalpha(lhc) && isalpha(rhc))
1448 return NSOrderedDescending;
1451 return [lhs caseInsensitiveCompare:rhs];
1454 - (NSComparisonResult) compareBySection:(Package *)package {
1455 NSString *lhs = [self section];
1456 NSString *rhs = [package section];
1458 if (lhs == NULL && rhs != NULL)
1459 return NSOrderedAscending;
1460 else if (lhs != NULL && rhs == NULL)
1461 return NSOrderedDescending;
1462 else if (lhs != NULL && rhs != NULL) {
1463 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1464 if (result != NSOrderedSame)
1468 return NSOrderedSame;
1471 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1472 NSString *lhs = [self section];
1473 NSString *rhs = [package section];
1475 if (lhs == NULL && rhs != NULL)
1476 return NSOrderedAscending;
1477 else if (lhs != NULL && rhs == NULL)
1478 return NSOrderedDescending;
1479 else if (lhs != NULL && rhs != NULL) {
1480 NSComparisonResult result = [lhs compare:rhs];
1481 if (result != NSOrderedSame)
1485 return [self compareByName:package];
1488 - (NSComparisonResult) compareForChanges:(Package *)package {
1489 BOOL lhs = [self upgradableAndEssential:YES];
1490 BOOL rhs = [package upgradableAndEssential:YES];
1493 return lhs ? NSOrderedAscending : NSOrderedDescending;
1495 switch ([[self seen] compare:[package seen]]) {
1496 case NSOrderedAscending:
1497 return NSOrderedDescending;
1500 case NSOrderedDescending:
1501 return NSOrderedAscending;
1507 return [self compareByName:package];
1511 pkgProblemResolver *resolver = [database_ resolver];
1512 resolver->Clear(iterator_);
1513 resolver->Protect(iterator_);
1514 pkgCacheFile &cache([database_ cache]);
1515 cache->MarkInstall(iterator_, false);
1516 pkgDepCache::StateCache &state((*cache)[iterator_]);
1517 if (!state.Install())
1518 cache->SetReInstall(iterator_, true);
1522 pkgProblemResolver *resolver = [database_ resolver];
1523 resolver->Clear(iterator_);
1524 resolver->Protect(iterator_);
1525 resolver->Remove(iterator_);
1526 [database_ cache]->MarkDelete(iterator_, true);
1529 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search {
1530 return [NSNumber numberWithBool:(
1531 [self valid] && [self visible] && [self matches:search]
1535 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1536 return [NSNumber numberWithBool:(
1537 (![number boolValue] || [self visible]) && [self installed] != nil
1541 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1542 NSString *section = [self section];
1544 return [NSNumber numberWithBool:(
1545 [self valid] && [self visible] &&
1546 [self installed] == nil && (
1548 section == nil && [name length] == 0 ||
1549 [name isEqualToString:section]
1554 - (NSNumber *) isVisibleInSource:(Source *)source {
1555 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1560 /* Section Class {{{ */
1561 @interface Section : NSObject {
1567 - (NSComparisonResult) compareByName:(Section *)section;
1568 - (Section *) initWithName:(NSString *)name;
1569 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1570 - (NSString *) name;
1573 - (void) addToCount;
1577 @implementation Section
1584 - (NSComparisonResult) compareByName:(Section *)section {
1585 NSString *lhs = [self name];
1586 NSString *rhs = [section name];
1588 if ([lhs length] != 0 && [rhs length] != 0) {
1589 unichar lhc = [lhs characterAtIndex:0];
1590 unichar rhc = [rhs characterAtIndex:0];
1592 if (isalpha(lhc) && !isalpha(rhc))
1593 return NSOrderedAscending;
1594 else if (!isalpha(lhc) && isalpha(rhc))
1595 return NSOrderedDescending;
1598 return [lhs caseInsensitiveCompare:rhs];
1601 - (Section *) initWithName:(NSString *)name {
1602 return [self initWithName:name row:0];
1605 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1606 if ((self = [super init]) != nil) {
1607 name_ = [name retain];
1612 - (NSString *) name {
1624 - (void) addToCount {
1631 /* Database Implementation {{{ */
1632 @implementation Database
1639 - (void) _readCydia:(NSNumber *)fd {
1640 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1642 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1643 std::istream is(&ib);
1646 while (std::getline(is, line)) {
1647 const char *data(line.c_str());
1648 //size_t size = line.size();
1649 fprintf(stderr, "C:%s\n", data);
1656 - (void) _readStatus:(NSNumber *)fd {
1657 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1659 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1660 std::istream is(&ib);
1663 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1664 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1666 while (std::getline(is, line)) {
1667 const char *data(line.c_str());
1668 size_t size = line.size();
1669 fprintf(stderr, "S:%s\n", data);
1671 if (conffile_r(data, size)) {
1672 [delegate_ setConfigurationData:conffile_r[1]];
1673 } else if (strncmp(data, "status: ", 8) == 0) {
1674 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1675 [delegate_ setProgressTitle:string];
1676 } else if (pmstatus_r(data, size)) {
1677 std::string type([pmstatus_r[1] UTF8String]);
1678 NSString *id = pmstatus_r[2];
1680 float percent([pmstatus_r[3] floatValue]);
1681 [delegate_ setProgressPercent:(percent / 100)];
1683 NSString *string = pmstatus_r[4];
1685 if (type == "pmerror")
1686 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1687 withObject:[NSArray arrayWithObjects:string, id, nil]
1690 else if (type == "pmstatus")
1691 [delegate_ setProgressTitle:string];
1692 else if (type == "pmconffile")
1693 [delegate_ setConfigurationData:string];
1694 else _assert(false);
1695 } else _assert(false);
1702 - (void) _readOutput:(NSNumber *)fd {
1703 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1705 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1706 std::istream is(&ib);
1709 while (std::getline(is, line)) {
1710 fprintf(stderr, "O:%s\n", line.c_str());
1711 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1722 - (Package *) packageWithName:(NSString *)name {
1723 if (static_cast<pkgDepCache *>(cache_) == NULL)
1725 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1726 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1729 - (Database *) init {
1730 if ((self = [super init]) != nil) {
1737 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1738 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1742 _assert(pipe(fds) != -1);
1745 _config->Set("APT::Keep-Fds::", cydiafd_);
1746 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 0"] UTF8String], _not(int));
1749 detachNewThreadSelector:@selector(_readCydia:)
1751 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1754 _assert(pipe(fds) != -1);
1758 detachNewThreadSelector:@selector(_readStatus:)
1760 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1763 _assert(pipe(fds) != -1);
1764 _assert(dup2(fds[0], 0) != -1);
1765 _assert(close(fds[0]) != -1);
1767 input_ = fdopen(fds[1], "a");
1769 _assert(pipe(fds) != -1);
1770 _assert(dup2(fds[1], 1) != -1);
1771 _assert(close(fds[1]) != -1);
1774 detachNewThreadSelector:@selector(_readOutput:)
1776 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1781 - (pkgCacheFile &) cache {
1785 - (pkgDepCache::Policy *) policy {
1789 - (pkgRecords *) records {
1793 - (pkgProblemResolver *) resolver {
1797 - (pkgAcquire &) fetcher {
1801 - (NSArray *) packages {
1805 - (NSArray *) sources {
1806 return [sources_ allValues];
1809 - (void) reloadData {
1828 if (!cache_.Open(progress_, true)) {
1830 if (!_error->PopMessage(error))
1833 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1835 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1836 [delegate_ repairWithSelector:@selector(configure)];
1837 else if (error == "The package lists or status file could not be parsed or opened.")
1838 [delegate_ repairWithSelector:@selector(update)];
1839 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1840 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1841 // else if (error == "The list of sources could not be read.")
1842 else _assert(false);
1847 now_ = [[NSDate date] retain];
1849 policy_ = new pkgDepCache::Policy();
1850 records_ = new pkgRecords(cache_);
1851 resolver_ = new pkgProblemResolver(cache_);
1852 fetcher_ = new pkgAcquire(&status_);
1855 list_ = new pkgSourceList();
1856 _assert(list_->ReadMainList());
1858 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1859 _assert(pkgApplyStatus(cache_));
1861 if (cache_->BrokenCount() != 0) {
1862 _assert(pkgFixBroken(cache_));
1863 _assert(cache_->BrokenCount() == 0);
1864 _assert(pkgMinimizeUpgrade(cache_));
1867 [sources_ removeAllObjects];
1868 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1869 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1870 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1872 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1873 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1877 [packages_ removeAllObjects];
1878 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1879 if (Package *package = [Package packageWithIterator:iterator database:self])
1880 [packages_ addObject:package];
1882 [packages_ sortUsingSelector:@selector(compareByName:)];
1885 - (void) configure {
1886 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1887 system([dpkg UTF8String]);
1895 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1896 _assert(!_error->PendingError());
1899 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1902 public pkgArchiveCleaner
1905 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1910 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1912 while (_error->PopMessage(error))
1913 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1918 pkgRecords records(cache_);
1920 lock_ = new FileFd();
1921 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1922 _assert(!_error->PendingError());
1925 // XXX: explain this with an error message
1926 _assert(list.ReadMainList());
1928 manager_ = (_system->CreatePM(cache_));
1929 _assert(manager_->GetArchives(fetcher_, &list, &records));
1930 _assert(!_error->PendingError());
1934 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1936 _assert(list.ReadMainList());
1937 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1938 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1941 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
1946 bool failed = false;
1947 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
1948 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
1951 std::string uri = (*item)->DescURI();
1952 std::string error = (*item)->ErrorText;
1954 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
1957 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1958 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
1969 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1971 if (_error->PendingError()) {
1976 if (result == pkgPackageManager::Failed) {
1981 if (result != pkgPackageManager::Completed) {
1986 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1988 _assert(list.ReadMainList());
1989 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1990 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1993 if (![before isEqualToArray:after])
1998 _assert(pkgDistUpgrade(cache_));
2002 [self updateWithStatus:status_];
2005 - (void) updateWithStatus:(Status &)status {
2007 _assert(list.ReadMainList());
2010 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2011 _assert(!_error->PendingError());
2013 pkgAcquire fetcher(&status);
2014 _assert(list.GetIndexes(&fetcher));
2016 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2017 bool failed = false;
2018 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2019 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2020 (*item)->Finished();
2024 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2025 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2026 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2029 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2034 - (void) setDelegate:(id)delegate {
2035 delegate_ = delegate;
2036 status_.setDelegate(delegate);
2037 progress_.setDelegate(delegate);
2040 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2041 pkgIndexFile *index(NULL);
2042 list_->FindIndex(file, index);
2043 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2049 /* Confirmation View {{{ */
2050 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2051 if ([packages count] == 0)
2054 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2055 [fields setObject:text forKey:key];
2057 CGColor blue(space_, 0, 0, 0.4, 1);
2058 [text setTextColor:blue];
2061 @protocol ConfirmationViewDelegate
2066 @interface ConfirmationView : UIView {
2067 Database *database_;
2069 UITransitionView *transition_;
2071 UINavigationBar *navbar_;
2072 UIPreferencesTable *table_;
2073 NSMutableDictionary *fields_;
2074 UIAlertSheet *essential_;
2079 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2083 @implementation ConfirmationView
2086 [navbar_ setDelegate:nil];
2087 [transition_ setDelegate:nil];
2088 [table_ setDataSource:nil];
2090 [transition_ release];
2095 if (essential_ != nil)
2096 [essential_ release];
2101 [transition_ transition:7 toView:nil];
2105 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2106 if (from != nil && to == nil)
2107 [self removeFromSuperview];
2110 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2113 if (essential_ != nil)
2114 [essential_ popupAlertAnimated:YES];
2116 [delegate_ confirm];
2125 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2126 NSString *context = [sheet context];
2128 if ([context isEqualToString:@"remove"])
2134 [delegate_ confirm];
2139 else if ([context isEqualToString:@"unable"])
2145 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2149 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2151 case 0: return @"Statistics";
2152 case 1: return @"Modifications";
2154 default: _assert(false);
2158 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2161 case 1: return [fields_ count];
2163 default: _assert(false);
2167 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2168 if (group != 1 || row == -1)
2171 _assert(size_t(row) < [fields_ count]);
2172 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2176 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2177 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2178 [cell setShowSelection:NO];
2181 case 0: switch (row) {
2183 [cell setTitle:@"Downloading"];
2184 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2188 [cell setTitle:@"Resuming At"];
2189 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2193 double size([database_ cache]->UsrSize());
2196 [cell setTitle:@"Disk Freeing"];
2197 [cell setValue:SizeString(-size)];
2199 [cell setTitle:@"Disk Using"];
2200 [cell setValue:SizeString(size)];
2204 default: _assert(false);
2208 _assert(size_t(row) < [fields_ count]);
2209 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2210 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2213 default: _assert(false);
2219 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2220 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2221 database_ = database;
2222 delegate_ = delegate;
2224 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2225 [self addSubview:transition_];
2227 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2229 CGSize navsize = [UINavigationBar defaultSize];
2230 CGRect navrect = {{0, 0}, navsize};
2231 CGRect bounds = [overlay_ bounds];
2233 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2235 [navbar_ setBarStyle:1];
2236 [navbar_ setDelegate:self];
2238 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2239 [navbar_ pushNavigationItem:navitem];
2240 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2242 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2244 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2245 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2246 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2247 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2248 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2252 pkgCacheFile &cache([database_ cache]);
2253 NSArray *packages = [database_ packages];
2254 for (size_t i(0), e = [packages count]; i != e; ++i) {
2255 Package *package = [packages objectAtIndex:i];
2256 pkgCache::PkgIterator iterator = [package iterator];
2257 pkgDepCache::StateCache &state(cache[iterator]);
2259 NSString *name([package name]);
2261 if (state.NewInstall())
2262 [installing addObject:name];
2263 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2264 [reinstalling addObject:name];
2265 else if (state.Upgrade())
2266 [upgrading addObject:name];
2267 else if (state.Downgrade())
2268 [downgrading addObject:name];
2269 else if (state.Delete()) {
2270 if ([package essential])
2272 [removing addObject:name];
2278 else if (Advanced_ || true) {
2279 essential_ = [[UIAlertSheet alloc]
2280 initWithTitle:@"Removing Essentials"
2281 buttons:[NSArray arrayWithObjects:
2282 @"Cancel Operation (Safe)",
2283 @"Force Removal (Unsafe)",
2285 defaultButtonIndex:0
2291 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2293 [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."];
2295 essential_ = [[UIAlertSheet alloc]
2296 initWithTitle:@"Unable to Comply"
2297 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2298 defaultButtonIndex:0
2303 [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."];
2306 AddTextView(fields_, installing, @"Installing");
2307 AddTextView(fields_, reinstalling, @"Reinstalling");
2308 AddTextView(fields_, upgrading, @"Upgrading");
2309 AddTextView(fields_, downgrading, @"Downgrading");
2310 AddTextView(fields_, removing, @"Removing");
2312 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2313 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2316 [table_ setReusesTableCells:YES];
2317 [table_ setDataSource:self];
2318 [table_ reloadData];
2320 [overlay_ addSubview:navbar_];
2321 [overlay_ addSubview:table_];
2323 [view addSubview:self];
2325 [transition_ setDelegate:self];
2327 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2328 [transition_ transition:0 toView:blank];
2329 [transition_ transition:3 toView:overlay_];
2336 /* Progress Data {{{ */
2337 @interface ProgressData : NSObject {
2343 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2350 @implementation ProgressData
2352 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2353 if ((self = [super init]) != nil) {
2354 selector_ = selector;
2374 /* Progress View {{{ */
2375 @interface ProgressView : UIView <
2376 ConfigurationDelegate,
2379 _transient Database *database_;
2381 UIView *background_;
2382 UITransitionView *transition_;
2384 UINavigationBar *navbar_;
2385 UIProgressBar *progress_;
2386 UITextView *output_;
2387 UITextLabel *status_;
2388 UIPushButton *close_;
2393 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2395 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2396 - (void) setContentView:(UIView *)view;
2399 - (void) _retachThread;
2400 - (void) _detachNewThreadData:(ProgressData *)data;
2401 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2407 @protocol ProgressViewDelegate
2408 - (void) progressViewIsComplete:(ProgressView *)sender;
2411 @implementation ProgressView
2414 [transition_ setDelegate:nil];
2415 [navbar_ setDelegate:nil];
2418 if (background_ != nil)
2419 [background_ release];
2420 [transition_ release];
2423 [progress_ release];
2430 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2431 if (bootstrap_ && from == overlay_ && to == view_)
2435 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2436 if ((self = [super initWithFrame:frame]) != nil) {
2437 database_ = database;
2438 delegate_ = delegate;
2440 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2441 [transition_ setDelegate:self];
2443 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2446 [overlay_ setBackgroundColor:Black_];
2448 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2449 [background_ setBackgroundColor:Black_];
2450 [self addSubview:background_];
2453 [self addSubview:transition_];
2455 CGSize navsize = [UINavigationBar defaultSize];
2456 CGRect navrect = {{0, 0}, navsize};
2458 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2459 [overlay_ addSubview:navbar_];
2461 [navbar_ setBarStyle:1];
2462 [navbar_ setDelegate:self];
2464 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2465 [navbar_ pushNavigationItem:navitem];
2467 CGRect bounds = [overlay_ bounds];
2468 CGSize prgsize = [UIProgressBar defaultSize];
2471 (bounds.size.width - prgsize.width) / 2,
2472 bounds.size.height - prgsize.height - 20
2475 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2476 [progress_ setStyle:0];
2478 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2480 bounds.size.height - prgsize.height - 50,
2481 bounds.size.width - 20,
2485 [status_ setColor:White_];
2486 [status_ setBackgroundColor:Clear_];
2488 [status_ setCentersHorizontally:YES];
2489 //[status_ setFont:font];
2491 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2493 navrect.size.height + 20,
2494 bounds.size.width - 20,
2495 bounds.size.height - navsize.height - 62 - navrect.size.height
2498 //[output_ setTextFont:@"Courier New"];
2499 [output_ setTextSize:12];
2501 [output_ setTextColor:White_];
2502 [output_ setBackgroundColor:Clear_];
2504 [output_ setMarginTop:0];
2505 [output_ setAllowsRubberBanding:YES];
2506 [output_ setEditable:NO];
2508 [overlay_ addSubview:output_];
2510 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2512 bounds.size.height - prgsize.height - 50,
2513 bounds.size.width - 20,
2517 [close_ setAutosizesToFit:NO];
2518 [close_ setDrawsShadow:YES];
2519 [close_ setStretchBackground:YES];
2520 [close_ setTitle:@"Return to Cydia"];
2521 [close_ setEnabled:YES];
2523 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2524 [close_ setTitleFont:bold];
2527 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2528 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2529 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2533 - (void) setContentView:(UIView *)view {
2534 view_ = [view retain];
2537 - (void) resetView {
2538 [transition_ transition:6 toView:view_];
2541 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2542 NSString *context = [sheet context];
2543 if ([context isEqualToString:@"conffile"]) {
2544 FILE *input = [database_ input];
2548 fprintf(input, "N\n");
2552 fprintf(input, "Y\n");
2563 - (void) closeButtonPushed {
2564 [delegate_ progressViewIsComplete:self];
2568 - (void) _retachThread {
2569 UINavigationItem *item = [navbar_ topItem];
2570 [item setTitle:@"Complete"];
2572 [overlay_ addSubview:close_];
2573 [progress_ removeFromSuperview];
2574 [status_ removeFromSuperview];
2577 notify_post("com.apple.mobile.application_installed");
2580 [delegate_ setStatusBarShowsProgress:NO];
2585 - (void) _detachNewThreadData:(ProgressData *)data {
2586 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2588 [[data target] performSelector:[data selector] withObject:[data object]];
2591 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2596 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2597 UINavigationItem *item = [navbar_ topItem];
2598 [item setTitle:title];
2600 [status_ setText:nil];
2601 [output_ setText:@""];
2602 [progress_ setProgress:0];
2604 [close_ removeFromSuperview];
2605 [overlay_ addSubview:progress_];
2606 [overlay_ addSubview:status_];
2608 [delegate_ setStatusBarShowsProgress:YES];
2611 [transition_ transition:6 toView:overlay_];
2614 detachNewThreadSelector:@selector(_detachNewThreadData:)
2616 withObject:[[ProgressData alloc]
2617 initWithSelector:selector
2624 - (void) repairWithSelector:(SEL)selector {
2626 detachNewThreadSelector:selector
2629 title:@"Repairing..."
2633 - (void) setConfigurationData:(NSString *)data {
2635 performSelectorOnMainThread:@selector(_setConfigurationData:)
2641 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2642 Package *package = id == nil ? nil : [database_ packageWithName:id];
2644 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2645 initWithTitle:(package == nil ? @"Source Error" : [package name])
2646 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2647 defaultButtonIndex:0
2652 [sheet setBodyText:error];
2653 [sheet popupAlertAnimated:YES];
2656 - (void) setProgressTitle:(NSString *)title {
2658 performSelectorOnMainThread:@selector(_setProgressTitle:)
2664 - (void) setProgressPercent:(float)percent {
2666 performSelectorOnMainThread:@selector(_setProgressPercent:)
2667 withObject:[NSNumber numberWithFloat:percent]
2672 - (void) addProgressOutput:(NSString *)output {
2674 performSelectorOnMainThread:@selector(_addProgressOutput:)
2680 - (void) _setConfigurationData:(NSString *)data {
2681 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2683 _assert(conffile_r(data));
2685 NSString *ofile = conffile_r[1];
2686 //NSString *nfile = conffile_r[2];
2688 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2689 initWithTitle:@"Configuration Upgrade"
2690 buttons:[NSArray arrayWithObjects:
2691 @"Keep My Old Copy",
2692 @"Accept The New Copy",
2693 // XXX: @"See What Changed",
2695 defaultButtonIndex:0
2700 [sheet setBodyText:[NSString stringWithFormat:
2701 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2704 [sheet popupAlertAnimated:YES];
2707 - (void) _setProgressTitle:(NSString *)title {
2708 [status_ setText:[title stringByAppendingString:@"..."]];
2711 - (void) _setProgressPercent:(NSNumber *)percent {
2712 [progress_ setProgress:[percent floatValue]];
2715 - (void) _addProgressOutput:(NSString *)output {
2716 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2717 CGSize size = [output_ contentSize];
2718 CGRect rect = {{0, size.height}, {size.width, 0}};
2719 [output_ scrollRectToVisible:rect animated:YES];
2722 - (BOOL) isRunning {
2729 /* Package Cell {{{ */
2730 @interface PackageCell : UISimpleTableCell {
2733 NSString *description_;
2735 //UIImageView *trusted_;
2737 UIImageView *badge_;
2738 UITextLabel *status_;
2743 - (PackageCell *) init;
2744 - (void) setPackage:(Package *)package;
2746 + (int) heightForPackage:(Package *)package;
2750 @implementation PackageCell
2752 - (void) clearPackage {
2763 if (description_ != nil) {
2764 [description_ release];
2768 if (source_ != nil) {
2775 [self clearPackage];
2780 //[trusted_ release];
2784 - (PackageCell *) init {
2785 if ((self = [super init]) != nil) {
2787 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2789 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2790 [status_ setBackgroundColor:Clear_];
2791 [status_ setFont:small];
2796 - (void) setPackage:(Package *)package {
2802 Source *source = [package source];
2805 if (NSString *icon = [package icon])
2806 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2807 if (icon_ == nil) if (NSString *section = [package section])
2808 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sections/%@.png", Simplify(section)]];
2809 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2810 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2812 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2814 icon_ = [icon_ retain];
2816 name_ = [[package name] retain];
2817 description_ = [[package tagline] retain];
2819 NSString *label = nil;
2820 bool trusted = false;
2822 if (source != nil) {
2823 label = [source label];
2824 trusted = [source trusted];
2825 } else if ([[package id] isEqualToString:@"firmware"])
2828 label = @"Unknown/Local";
2830 NSString *from = [NSString stringWithFormat:@"from %@", label];
2832 NSString *section = Simplify([package section]);
2833 if (section != nil && ![section isEqualToString:label])
2834 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2836 source_ = [from retain];
2839 [badge_ removeFromSuperview];
2840 [status_ removeFromSuperview];
2842 if (NSString *mode = [package mode]) {
2843 [badge_ setImage:[UIImage applicationImageNamed:
2844 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2847 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2848 [status_ setColor:Blueish_];
2849 } else if ([package half]) {
2850 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2851 [status_ setText:@"Package Damaged"];
2852 [status_ setColor:Red_];
2854 [badge_ setImage:nil];
2855 [status_ setText:nil];
2859 [self addSubview:badge_];
2860 [self addSubview:status_];
2865 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2867 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
2874 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
2875 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
2879 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
2881 [super drawContentInRect:rect selected:selected];
2884 + (int) heightForPackage:(Package *)package {
2885 NSString *tagline([package tagline]);
2886 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
2888 if ([package hasMode] || [package half])
2897 /* Section Cell {{{ */
2898 @interface SectionCell : UISimpleTableCell {
2903 UISwitchControl *switch_;
2908 - (void) setSection:(Section *)section editing:(BOOL)editing;
2912 @implementation SectionCell
2914 - (void) clearSection {
2915 if (section_ != nil) {
2925 if (count_ != nil) {
2932 [self clearSection];
2939 if ((self = [super init]) != nil) {
2940 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
2942 switch_ = [[UISwitchControl alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
2943 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
2947 - (void) onSwitch:(id)sender {
2948 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
2949 if (metadata == nil) {
2950 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
2951 [Sections_ setObject:metadata forKey:section_];
2955 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
2958 - (void) setSection:(Section *)section editing:(BOOL)editing {
2959 if (editing != editing_) {
2961 [switch_ removeFromSuperview];
2963 [self addSubview:switch_];
2967 [self clearSection];
2969 if (section == nil) {
2970 name_ = [@"All Packages" retain];
2973 section_ = [section name];
2974 if (section_ != nil)
2975 section_ = [section_ retain];
2976 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
2977 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
2980 [switch_ setValue:isSectionVisible(section_) animated:NO];
2984 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2985 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
2992 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
2994 CGSize size = [count_ sizeWithFont:Font14_];
2998 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3000 [super drawContentInRect:rect selected:selected];
3006 /* File Table {{{ */
3007 @interface FileTable : RVPage {
3008 _transient Database *database_;
3011 NSMutableArray *files_;
3015 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3016 - (void) setPackage:(Package *)package;
3020 @implementation FileTable
3023 if (package_ != nil)
3032 - (int) numberOfRowsInTable:(UITable *)table {
3033 return files_ == nil ? 0 : [files_ count];
3036 - (float) table:(UITable *)table heightForRow:(int)row {
3040 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3041 if (reusing == nil) {
3042 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3043 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3044 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3047 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3051 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3055 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3056 if ((self = [super initWithBook:book]) != nil) {
3057 database_ = database;
3059 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3061 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3062 [self addSubview:list_];
3064 UITableColumn *column = [[[UITableColumn alloc]
3065 initWithTitle:@"Name"
3067 width:[self frame].size.width
3070 [list_ setDataSource:self];
3071 [list_ setSeparatorStyle:1];
3072 [list_ addTableColumn:column];
3073 [list_ setDelegate:self];
3074 [list_ setReusesTableCells:YES];
3078 - (void) setPackage:(Package *)package {
3079 if (package_ != nil) {
3080 [package_ autorelease];
3089 [files_ removeAllObjects];
3091 if (package != nil) {
3092 package_ = [package retain];
3093 name_ = [[package id] retain];
3095 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3098 std::ifstream fin([path UTF8String]);
3100 while (std::getline(fin, line))
3101 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3104 if ([files_ count] != 0) {
3105 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3106 [files_ removeObjectAtIndex:0];
3107 [files_ sortUsingSelector:@selector(compareByPath:)];
3109 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3110 [stack addObject:@"/"];
3112 for (int i(0), e([files_ count]); i != e; ++i) {
3113 NSString *file = [files_ objectAtIndex:i];
3114 while (![file hasPrefix:[stack lastObject]])
3115 [stack removeLastObject];
3116 NSString *directory = [stack lastObject];
3117 [stack addObject:[file stringByAppendingString:@"/"]];
3118 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3119 ([stack count] - 2) * 3, "",
3120 [file substringFromIndex:[directory length]]
3129 - (void) resetViewAnimated:(BOOL)animated {
3130 [list_ resetViewAnimated:animated];
3133 - (void) reloadData {
3134 [self setPackage:[database_ packageWithName:name_]];
3135 [self reloadButtons];
3138 - (NSString *) title {
3139 return @"Installed Files";
3142 - (NSString *) backButtonTitle {
3148 /* Package View {{{ */
3149 @protocol PackageViewDelegate
3150 - (void) performPackage:(Package *)package;
3153 @interface PackageView : RVPage {
3154 _transient Database *database_;
3155 UIPreferencesTable *table_;
3158 UITextView *description_;
3159 NSMutableArray *buttons_;
3162 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3163 - (void) setPackage:(Package *)package;
3167 @implementation PackageView
3170 [table_ setDataSource:nil];
3171 [table_ setDelegate:nil];
3173 if (package_ != nil)
3177 if (description_ != nil)
3178 [description_ release];
3184 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
3186 if ([package_ installed] != nil)
3188 if ([package_ source] != nil)
3193 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
3196 else if ([package_ installed] != nil && group-- == 0)
3197 return @"Installed Package";
3198 else if (group-- == 0)
3199 return @"Package Details";
3200 else if ([package_ source] != nil && group-- == 0)
3201 return @"Source Information";
3202 else _assert(false);
3205 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
3206 if (description_ == nil || group != 0 || row != ([package_ author] == nil ? 1 : 2))
3209 return [description_ visibleTextRect].size.height + TextViewOffset_;
3212 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3215 if ([package_ author] != nil)
3217 if (description_ != nil)
3219 if ([package_ website] != nil)
3222 } else if ([package_ installed] != nil && group-- == 0)
3224 else if (group-- == 0) {
3226 if ([package_ size] != 0)
3228 if ([package_ maintainer] != nil)
3230 if ([package_ sponsor] != nil)
3232 if ([package_ relationships] != nil)
3234 if ([[package_ source] trusted])
3237 } else if ([package_ source] != nil && group-- == 0) {
3238 Source *source = [package_ source];
3239 NSString *description = [source description];
3241 if (description != nil && ![description isEqualToString:[source label]])
3243 if ([source origin] != nil)
3246 } else _assert(false);
3249 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3250 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3251 [cell setShowSelection:NO];
3255 } else if (row-- == 0) {
3256 [cell setTitle:[package_ name]];
3257 [cell setValue:[package_ latest]];
3258 } else if ([package_ author] != nil && row-- == 0) {
3259 [cell setTitle:@"Author"];
3260 [cell setValue:[[package_ author] name]];
3261 [cell setShowDisclosure:YES];
3262 [cell setShowSelection:YES];
3263 } else if (description_ != nil && row-- == 0) {
3264 [cell addSubview:description_];
3265 } else if ([package_ website] != nil && row-- == 0) {
3266 [cell setTitle:@"More Information"];
3267 [cell setShowDisclosure:YES];
3268 [cell setShowSelection:YES];
3269 } else _assert(false);
3270 } else if ([package_ installed] != nil && group-- == 0) {
3272 } else if (row-- == 0) {
3273 [cell setTitle:@"Version"];
3274 NSString *installed([package_ installed]);
3275 [cell setValue:(installed == nil ? @"n/a" : installed)];
3276 } else if (row-- == 0) {
3277 [cell setTitle:@"Filesystem Content"];
3278 [cell setShowDisclosure:YES];
3279 [cell setShowSelection:YES];
3280 } else _assert(false);
3281 } else if (group-- == 0) {
3283 } else if (row-- == 0) {
3284 [cell setTitle:@"Identifier"];
3285 [cell setValue:[package_ id]];
3286 } else if (row-- == 0) {
3287 [cell setTitle:@"Section"];
3288 NSString *section([package_ section]);
3289 [cell setValue:(section == nil ? @"n/a" : section)];
3290 } else if ([package_ size] != 0 && row-- == 0) {
3291 [cell setTitle:@"Expanded Size"];
3292 [cell setValue:SizeString([package_ size])];
3293 } else if ([package_ maintainer] != nil && row-- == 0) {
3294 [cell setTitle:@"Maintainer"];
3295 [cell setValue:[[package_ maintainer] name]];
3296 [cell setShowDisclosure:YES];
3297 [cell setShowSelection:YES];
3298 } else if ([package_ sponsor] != nil && row-- == 0) {
3299 [cell setTitle:@"Sponsor"];
3300 [cell setValue:[[package_ sponsor] name]];
3301 [cell setShowDisclosure:YES];
3302 [cell setShowSelection:YES];
3303 } else if ([package_ relationships] != nil && row-- == 0) {
3304 [cell setTitle:@"Package Relationships"];
3305 [cell setShowDisclosure:YES];
3306 [cell setShowSelection:YES];
3307 } else if ([[package_ source] trusted] && row-- == 0) {
3308 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3309 [cell setValue:@"This package has been signed."];
3310 } else _assert(false);
3311 } else if ([package_ source] != nil && group-- == 0) {
3312 Source *source = [package_ source];
3313 NSString *description = [source description];
3316 } else if (row-- == 0) {
3317 NSString *label = [source label];
3319 label = [source uri];
3320 [cell setTitle:label];
3321 [cell setValue:[source version]];
3322 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3323 [cell setValue:description];
3324 } else if ([source origin] != nil && row-- == 0) {
3325 [cell setTitle:@"Origin"];
3326 [cell setValue:[source origin]];
3327 } else _assert(false);
3328 } else _assert(false);
3333 - (BOOL) canSelectRow:(int)row {
3337 - (void) tableRowSelected:(NSNotification *)notification {
3338 int row = [table_ selectedRow];
3342 #define _else else goto _label; return; } _label:
3346 } else if (row-- == 0) {
3347 } else if ([package_ author] != nil && row-- == 0) {
3348 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3349 [[package_ author] email],
3350 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3352 ] stringByAddingPercentEscapes]
3354 } else if (description_ != nil && row-- == 0) {
3355 } else if ([package_ website] != nil && row-- == 0) {
3356 NSURL *url = [NSURL URLWithString:[package_ website]];
3357 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3358 [browser setDelegate:delegate_];
3359 [book_ pushPage:browser];
3360 [browser loadURL:url];
3361 } _else if ([package_ installed] != nil) {
3363 } else if (row-- == 0) {
3364 } else if (row-- == 0) {
3365 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3366 [files setDelegate:delegate_];
3367 [files setPackage:package_];
3368 [book_ pushPage:files];
3371 } else if (row-- == 0) {
3372 } else if (row-- == 0) {
3373 } else if ([package_ size] != 0 && row-- == 0) {
3374 } else if ([package_ maintainer] != nil && row-- == 0) {
3375 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3376 [[package_ maintainer] email],
3377 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3379 ] stringByAddingPercentEscapes]
3381 } else if ([package_ sponsor] != nil && row-- == 0) {
3382 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3383 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3384 [browser setDelegate:delegate_];
3385 [book_ pushPage:browser];
3386 [browser loadURL:url];
3387 } else if ([package_ relationships] != nil && row-- == 0) {
3388 } else if ([[package_ source] trusted] && row-- == 0) {
3389 } _else if ([package_ source] != nil) {
3390 Source *source = [package_ source];
3391 NSString *description = [source description];
3394 } else if (row-- == 0) {
3395 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3396 } else if ([source origin] != nil && row-- == 0) {
3397 } _else _assert(false);
3402 - (void) _clickButtonWithName:(NSString *)name {
3403 if ([name isEqualToString:@"Install"])
3404 [delegate_ installPackage:package_];
3405 else if ([name isEqualToString:@"Reinstall"])
3406 [delegate_ installPackage:package_];
3407 else if ([name isEqualToString:@"Remove"])
3408 [delegate_ removePackage:package_];
3409 else if ([name isEqualToString:@"Upgrade"])
3410 [delegate_ installPackage:package_];
3411 else _assert(false);
3414 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3415 int count = [buttons_ count];
3416 _assert(count != 0);
3417 _assert(button <= count + 1);
3419 if (count != button - 1)
3420 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3425 - (void) _rightButtonClicked {
3426 int count = [buttons_ count];
3427 _assert(count != 0);
3430 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3432 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3433 [buttons addObjectsFromArray:buttons_];
3434 [buttons addObject:@"Cancel"];
3436 [delegate_ slideUp:[[[UIAlertSheet alloc]
3439 defaultButtonIndex:2
3446 - (NSString *) rightButtonTitle {
3447 int count = [buttons_ count];
3448 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3451 - (NSString *) title {
3455 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3456 if ((self = [super initWithBook:book]) != nil) {
3457 database_ = database;
3459 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3460 [self addSubview:table_];
3462 [table_ setDataSource:self];
3463 [table_ setDelegate:self];
3465 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3469 - (void) setPackage:(Package *)package {
3470 if (package_ != nil) {
3471 [package_ autorelease];
3480 if (description_ != nil) {
3481 [description_ release];
3485 [buttons_ removeAllObjects];
3487 if (package != nil) {
3488 package_ = [package retain];
3489 name_ = [[package id] retain];
3491 NSString *description([package description]);
3492 if (description == nil)
3493 description = [package tagline];
3494 if (description != nil) {
3495 description_ = [GetTextView(description, 12, true) retain];
3496 [description_ setTextColor:Black_];
3499 [table_ reloadData];
3501 if ([package_ source] == nil);
3502 else if ([package_ upgradableAndEssential:NO])
3503 [buttons_ addObject:@"Upgrade"];
3504 else if ([package_ installed] == nil)
3505 [buttons_ addObject:@"Install"];
3507 [buttons_ addObject:@"Reinstall"];
3508 if ([package_ installed] != nil)
3509 [buttons_ addObject:@"Remove"];
3513 - (void) resetViewAnimated:(BOOL)animated {
3514 [table_ resetViewAnimated:animated];
3517 - (void) reloadData {
3518 [self setPackage:[database_ packageWithName:name_]];
3519 [self reloadButtons];
3524 /* Package Table {{{ */
3525 @interface PackageTable : RVPage {
3526 _transient Database *database_;
3530 NSMutableArray *packages_;
3531 NSMutableArray *sections_;
3532 UISectionList *list_;
3535 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3537 - (void) setDelegate:(id)delegate;
3538 - (void) setObject:(id)object;
3540 - (void) reloadData;
3541 - (void) resetCursor;
3543 - (UISectionList *) list;
3545 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3549 @implementation PackageTable
3552 [list_ setDataSource:nil];
3557 [packages_ release];
3558 [sections_ release];
3563 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3564 return [sections_ count];
3567 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3568 return [[sections_ objectAtIndex:section] name];
3571 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3572 return [[sections_ objectAtIndex:section] row];
3575 - (int) numberOfRowsInTable:(UITable *)table {
3576 return [packages_ count];
3579 - (float) table:(UITable *)table heightForRow:(int)row {
3580 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3583 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3585 reusing = [[[PackageCell alloc] init] autorelease];
3586 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3590 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3594 - (void) tableRowSelected:(NSNotification *)notification {
3595 int row = [[notification object] selectedRow];
3599 Package *package = [packages_ objectAtIndex:row];
3600 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3601 [view setDelegate:delegate_];
3602 [view setPackage:package];
3603 [book_ pushPage:view];
3606 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3607 if ((self = [super initWithBook:book]) != nil) {
3608 database_ = database;
3609 title_ = [title retain];
3611 object_ = object == nil ? nil : [object retain];
3613 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3614 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3616 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3617 [list_ setDataSource:self];
3619 UITableColumn *column = [[[UITableColumn alloc]
3620 initWithTitle:@"Name"
3622 width:[self frame].size.width
3625 UITable *table = [list_ table];
3626 [table setSeparatorStyle:1];
3627 [table addTableColumn:column];
3628 [table setDelegate:self];
3629 [table setReusesTableCells:YES];
3631 [self addSubview:list_];
3636 - (void) setDelegate:(id)delegate {
3637 delegate_ = delegate;
3640 - (void) setObject:(id)object {
3646 object_ = [object retain];
3649 - (void) reloadData {
3650 NSArray *packages = [database_ packages];
3652 [packages_ removeAllObjects];
3653 [sections_ removeAllObjects];
3655 for (size_t i(0); i != [packages count]; ++i) {
3656 Package *package([packages objectAtIndex:i]);
3657 if ([[package performSelector:filter_ withObject:object_] boolValue])
3658 [packages_ addObject:package];
3661 Section *section = nil;
3663 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3664 Package *package = [packages_ objectAtIndex:offset];
3665 NSString *name = [package index];
3667 if (section == nil || ![[section name] isEqualToString:name]) {
3668 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3669 [sections_ addObject:section];
3672 [section addToCount];
3678 - (NSString *) title {
3682 - (void) resetViewAnimated:(BOOL)animated {
3683 [list_ resetViewAnimated:animated];
3686 - (void) resetCursor {
3687 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3690 - (UISectionList *) list {
3694 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3695 [list_ setShouldHideHeaderInShortLists:hide];
3701 /* Add Source View {{{ */
3702 @interface AddSourceView : RVPage {
3703 _transient Database *database_;
3706 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3710 @implementation AddSourceView
3712 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3713 if ((self = [super initWithBook:book]) != nil) {
3714 database_ = database;
3720 /* Source Cell {{{ */
3721 @interface SourceCell : UITableCell {
3724 NSString *description_;
3730 - (SourceCell *) initWithSource:(Source *)source;
3734 @implementation SourceCell
3739 [description_ release];
3744 - (SourceCell *) initWithSource:(Source *)source {
3745 if ((self = [super init]) != nil) {
3747 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3749 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3750 icon_ = [icon_ retain];
3752 origin_ = [[source name] retain];
3753 label_ = [[source uri] retain];
3754 description_ = [[source description] retain];
3758 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3760 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3767 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3771 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3775 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3777 [super drawContentInRect:rect selected:selected];
3782 /* Source Table {{{ */
3783 @interface SourceTable : RVPage {
3784 _transient Database *database_;
3785 UISectionList *list_;
3786 NSMutableArray *sources_;
3787 UIAlertSheet *alert_;
3791 UIProgressHUD *hud_;
3794 //NSURLConnection *installer_;
3795 NSURLConnection *trivial_bz2_;
3796 NSURLConnection *trivial_gz_;
3797 //NSURLConnection *automatic_;
3802 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3806 @implementation SourceTable
3808 - (void) _deallocConnection:(NSURLConnection *)connection {
3809 if (connection != nil) {
3810 [connection cancel];
3811 //[connection setDelegate:nil];
3812 [connection release];
3817 [[list_ table] setDelegate:nil];
3818 [list_ setDataSource:nil];
3827 //[self _deallocConnection:installer_];
3828 [self _deallocConnection:trivial_gz_];
3829 [self _deallocConnection:trivial_bz2_];
3830 //[self _deallocConnection:automatic_];
3837 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3838 return offset_ == 0 ? 1 : 2;
3841 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3842 switch (section + (offset_ == 0 ? 1 : 0)) {
3843 case 0: return @"Entered by User";
3844 case 1: return @"Installed by Packages";
3852 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3853 switch (section + (offset_ == 0 ? 1 : 0)) {
3855 case 1: return offset_;
3863 - (int) numberOfRowsInTable:(UITable *)table {
3864 return [sources_ count];
3867 - (float) table:(UITable *)table heightForRow:(int)row {
3868 Source *source = [sources_ objectAtIndex:row];
3869 return [source description] == nil ? 56 : 73;
3872 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3873 Source *source = [sources_ objectAtIndex:row];
3874 // XXX: weird warning, stupid selectors ;P
3875 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3878 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3882 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3886 - (void) tableRowSelected:(NSNotification*)notification {
3887 UITable *table([list_ table]);
3888 int row([table selectedRow]);
3892 Source *source = [sources_ objectAtIndex:row];
3894 PackageTable *packages = [[[PackageTable alloc]
3897 title:[source label]
3898 filter:@selector(isVisibleInSource:)
3902 [packages setDelegate:delegate_];
3904 [book_ pushPage:packages];
3907 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3908 Source *source = [sources_ objectAtIndex:row];
3909 return [source record] != nil;
3912 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3913 [[list_ table] setDeleteConfirmationRow:row];
3916 - (void) table:(UITable *)table deleteRow:(int)row {
3917 Source *source = [sources_ objectAtIndex:row];
3918 [Sources_ removeObjectForKey:[source key]];
3919 [delegate_ syncData];
3922 - (void) _endConnection:(NSURLConnection *)connection {
3923 NSURLConnection **field = NULL;
3924 if (connection == trivial_bz2_)
3925 field = &trivial_bz2_;
3926 else if (connection == trivial_gz_)
3927 field = &trivial_gz_;
3928 _assert(field != NULL);
3929 [connection release];
3933 trivial_bz2_ == nil &&
3936 [delegate_ setStatusBarShowsProgress:NO];
3939 [hud_ removeFromSuperview];
3944 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3947 @"./", @"Distribution",
3948 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3950 [delegate_ syncData];
3951 } else if (error_ != nil) {
3952 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3953 initWithTitle:@"Verification Error"
3954 buttons:[NSArray arrayWithObjects:@"OK", nil]
3955 defaultButtonIndex:0
3960 [sheet setBodyText:[error_ localizedDescription]];
3961 [sheet popupAlertAnimated:YES];
3963 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3964 initWithTitle:@"Did not Find Repository"
3965 buttons:[NSArray arrayWithObjects:@"OK", nil]
3966 defaultButtonIndex:0
3971 [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."];
3972 [sheet popupAlertAnimated:YES];
3978 if (error_ != nil) {
3985 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3986 switch ([response statusCode]) {
3992 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3993 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3995 error_ = [error retain];
3996 [self _endConnection:connection];
3999 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4000 [self _endConnection:connection];
4003 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4004 NSMutableURLRequest *request = [NSMutableURLRequest
4005 requestWithURL:[NSURL URLWithString:href]
4006 cachePolicy:NSURLRequestUseProtocolCachePolicy
4007 timeoutInterval:20.0
4010 [request setHTTPMethod:method];
4012 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4015 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4016 NSString *context = [sheet context];
4017 if ([context isEqualToString:@"source"])
4020 NSString *href = [[sheet textField] text];
4022 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4024 if (![href hasSuffix:@"/"])
4025 href_ = [href stringByAppendingString:@"/"];
4028 href_ = [href_ retain];
4030 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4031 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4032 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4036 hud_ = [delegate_ addProgressHUD];
4037 [hud_ setText:@"Verifying URL"];
4050 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4051 if ((self = [super initWithBook:book]) != nil) {
4052 database_ = database;
4053 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4055 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4056 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4057 [list_ setShouldHideHeaderInShortLists:NO];
4059 [self addSubview:list_];
4060 [list_ setDataSource:self];
4062 UITableColumn *column = [[UITableColumn alloc]
4063 initWithTitle:@"Name"
4065 width:[self frame].size.width
4068 UITable *table = [list_ table];
4069 [table setSeparatorStyle:1];
4070 [table addTableColumn:column];
4071 [table setDelegate:self];
4077 - (void) reloadData {
4079 _assert(list.ReadMainList());
4081 [sources_ removeAllObjects];
4082 [sources_ addObjectsFromArray:[database_ sources]];
4083 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4085 int count = [sources_ count];
4086 for (offset_ = 0; offset_ != count; ++offset_) {
4087 Source *source = [sources_ objectAtIndex:offset_];
4088 if ([source record] == nil)
4095 - (void) resetViewAnimated:(BOOL)animated {
4096 [list_ resetViewAnimated:animated];
4099 - (void) _leftButtonClicked {
4100 /*[book_ pushPage:[[[AddSourceView alloc]
4105 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4106 initWithTitle:@"Enter Cydia/APT URL"
4107 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4108 defaultButtonIndex:0
4113 [sheet addTextFieldWithValue:@"http://" label:@""];
4115 UITextTraits *traits = [[sheet textField] textTraits];
4116 [traits setAutoCapsType:0];
4117 [traits setPreferredKeyboardType:3];
4118 [traits setAutoCorrectionType:1];
4120 [sheet popupAlertAnimated:YES];
4123 - (void) _rightButtonClicked {
4124 UITable *table = [list_ table];
4125 BOOL editing = [table isRowDeletionEnabled];
4126 [table enableRowDeletion:!editing animated:YES];
4127 [book_ reloadButtonsForPage:self];
4130 - (NSString *) title {
4134 - (NSString *) backButtonTitle {
4138 - (NSString *) leftButtonTitle {
4139 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4142 - (NSString *) rightButtonTitle {
4143 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4146 - (RVUINavBarButtonStyle) rightButtonStyle {
4147 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4153 /* Installed View {{{ */
4154 @interface InstalledView : RVPage {
4155 _transient Database *database_;
4156 PackageTable *packages_;
4160 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4164 @implementation InstalledView
4167 [packages_ release];
4171 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4172 if ((self = [super initWithBook:book]) != nil) {
4173 database_ = database;
4175 packages_ = [[PackageTable alloc]
4179 filter:@selector(isInstalledAndVisible:)
4180 with:[NSNumber numberWithBool:YES]
4183 [self addSubview:packages_];
4187 - (void) resetViewAnimated:(BOOL)animated {
4188 [packages_ resetViewAnimated:animated];
4191 - (void) reloadData {
4192 [packages_ reloadData];
4195 - (void) _rightButtonClicked {
4196 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4197 [packages_ reloadData];
4199 [book_ reloadButtonsForPage:self];
4202 - (NSString *) title {
4203 return @"Installed";
4206 - (NSString *) backButtonTitle {
4210 - (NSString *) rightButtonTitle {
4211 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4214 - (RVUINavBarButtonStyle) rightButtonStyle {
4215 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4218 - (void) setDelegate:(id)delegate {
4219 [super setDelegate:delegate];
4220 [packages_ setDelegate:delegate];
4226 @interface HomeView : BrowserView {
4231 @implementation HomeView
4233 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4237 - (void) _leftButtonClicked {
4238 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4239 initWithTitle:@"About Cydia Installer"
4240 buttons:[NSArray arrayWithObjects:@"Close", nil]
4241 defaultButtonIndex:0
4247 @"Copyright (C) 2008\n"
4248 "Jay Freeman (saurik)\n"
4249 "saurik@saurik.com\n"
4250 "http://www.saurik.com/\n"
4253 "http://www.theokorigroup.com/\n"
4255 "College of Creative Studies,\n"
4256 "University of California,\n"
4258 "http://www.ccs.ucsb.edu/"
4261 [sheet popupAlertAnimated:YES];
4264 - (NSString *) leftButtonTitle {
4270 @interface ManageView : BrowserView {
4275 @implementation ManageView
4277 - (NSString *) title {
4281 - (void) _leftButtonClicked {
4282 [delegate_ askForSettings];
4285 - (NSString *) leftButtonTitle {
4289 - (NSString *) rightButtonTitle {
4295 /* Browser Implementation {{{ */
4296 @implementation BrowserView
4299 WebView *webview = [webview_ webView];
4300 [webview setFrameLoadDelegate:nil];
4301 [webview setResourceLoadDelegate:nil];
4302 [webview setUIDelegate:nil];
4304 [scroller_ setDelegate:nil];
4305 [webview_ setDelegate:nil];
4307 [scroller_ release];
4310 [indicator_ release];
4316 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4317 [self loadRequest:[NSURLRequest
4320 timeoutInterval:30.0
4324 - (void) loadURL:(NSURL *)url {
4325 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4328 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4329 NSMutableURLRequest *copy = [request mutableCopy];
4331 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4332 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4333 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4336 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4341 - (void) loadRequest:(NSURLRequest *)request {
4343 [webview_ loadRequest:request];
4346 - (void) reloadURL {
4347 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4348 [urls_ removeLastObject];
4349 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4352 - (WebView *) webView {
4353 return [webview_ webView];
4356 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4357 [scroller_ setContentSize:frame.size];
4360 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4361 [self view:sender didSetFrame:frame];
4364 - (void) pushPage:(RVPage *)page {
4365 [self setBackButtonTitle:title_];
4366 [page setDelegate:delegate_];
4367 [book_ pushPage:page];
4370 - (void) getSpecial:(NSString *)href {
4373 if ([href hasPrefix:@"mailto:"])
4374 [delegate_ openURL:[NSURL URLWithString:href]];
4375 else if ([href isEqualToString:@"cydia://add-source"])
4376 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4377 else if ([href isEqualToString:@"cydia://sources"])
4378 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4379 else if ([href isEqualToString:@"cydia://packages"])
4380 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4381 else if ([href hasPrefix:@"apptapp://package/"]) {
4382 NSString *name = [href substringFromIndex:18];
4384 if (Package *package = [database_ packageWithName:name]) {
4385 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4386 [view setPackage:package];
4389 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4390 initWithTitle:@"Cannot Locate Package"
4391 buttons:[NSArray arrayWithObjects:@"Close", nil]
4392 defaultButtonIndex:0
4397 [sheet setBodyText:[NSString stringWithFormat:
4398 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4401 [sheet popupAlertAnimated:YES];
4406 [self pushPage:page];
4409 - (void) webView:(WebView *)sender willClickElement:(id)element {
4410 if ([[element localName] isEqualToString:@"img"])
4411 do if ((element = [element parentNode]) == nil)
4413 while (![[element localName] isEqualToString:@"a"]);
4414 if (![element respondsToSelector:@selector(href)])
4416 NSString *href = [element href];
4419 [self getSpecial:href];
4422 - (BOOL) isSpecialScheme:(NSString *)scheme {
4424 [scheme isEqualToString:@"apptapp"] ||
4425 [scheme isEqualToString:@"cydia"] ||
4426 [scheme isEqualToString:@"mailto"];
4429 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4430 NSURL *url = [request URL];
4431 if ([self isSpecialScheme:[url scheme]]) {
4432 [self getSpecial:[url absoluteString]];
4438 [book_ pushPage:self];
4441 return [self _addHeadersToRequest:request];
4444 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4445 if (request != nil) {
4446 NSString *scheme = [[request URL] scheme];
4447 if ([self isSpecialScheme:scheme])
4451 [self setBackButtonTitle:title_];
4453 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4454 [browser setDelegate:delegate_];
4456 if (request != nil) {
4457 [browser loadRequest:[self _addHeadersToRequest:request]];
4458 [book_ pushPage:browser];
4461 return [browser webView];
4464 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4465 title_ = [title retain];
4466 [self setTitle:title];
4469 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4470 if ([frame parentFrame] != nil)
4475 [indicator_ startAnimation];
4476 [self reloadButtons];
4478 if (title_ != nil) {
4483 [self setTitle:@"Loading..."];
4485 WebView *webview = [webview_ webView];
4486 NSString *href = [webview mainFrameURL];
4487 [urls_ addObject:[NSURL URLWithString:href]];
4489 CGRect webrect = [scroller_ frame];
4490 webrect.size.height = 0;
4491 [webview_ setFrame:webrect];
4494 - (void) _finishLoading {
4497 [indicator_ stopAnimation];
4498 [self reloadButtons];
4502 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4503 if ([frame parentFrame] != nil)
4505 [self _finishLoading];
4508 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4509 if ([frame parentFrame] != nil)
4511 [self _finishLoading];
4513 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4514 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4515 [[error localizedDescription] stringByAddingPercentEscapes]
4519 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4520 if ((self = [super initWithBook:book]) != nil) {
4521 database_ = database;
4524 struct CGRect bounds = [self bounds];
4526 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4527 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4528 [self addSubview:pinstripe];
4530 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4531 [self addSubview:scroller_];
4533 [scroller_ setScrollingEnabled:YES];
4534 [scroller_ setAdjustForContentSizeChange:YES];
4535 [scroller_ setClipsSubviews:YES];
4536 [scroller_ setAllowsRubberBanding:YES];
4537 [scroller_ setScrollDecelerationFactor:0.99];
4538 [scroller_ setDelegate:self];
4540 CGRect webrect = [scroller_ bounds];
4541 webrect.size.height = 0;
4543 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4544 [scroller_ addSubview:webview_];
4546 [webview_ setTilingEnabled:YES];
4547 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4548 [webview_ setAutoresizes:YES];
4549 [webview_ setDelegate:self];
4550 //[webview_ setEnabledGestures:2];
4552 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4553 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4554 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4556 Package *package([database_ packageWithName:@"cydia"]);
4557 NSString *application = package == nil ? @"Cydia" : [NSString
4558 stringWithFormat:@"Cydia/%@",
4562 WebView *webview = [webview_ webView];
4563 [webview setApplicationNameForUserAgent:application];
4564 [webview setFrameLoadDelegate:self];
4565 [webview setResourceLoadDelegate:self];
4566 [webview setUIDelegate:self];
4568 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4572 - (void) _rightButtonClicked {
4577 - (NSString *) rightButtonTitle {
4578 return loading_ ? @"" : @"Reload";
4581 - (NSString *) title {
4585 - (NSString *) backButtonTitle {
4589 - (void) setPageActive:(BOOL)active {
4591 [book_ addSubview:indicator_];
4593 [indicator_ removeFromSuperview];
4596 - (void) resetViewAnimated:(BOOL)animated {
4599 - (void) setPushed:(bool)pushed {
4606 @interface CYBook : RVBook <
4609 _transient Database *database_;
4611 UIProgressIndicator *indicator_;
4612 UITextLabel *prompt_;
4613 UIProgressBar *progress_;
4617 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4623 /* Install View {{{ */
4624 @interface InstallView : RVPage {
4625 _transient Database *database_;
4626 NSMutableArray *sections_;
4627 NSMutableArray *filtered_;
4628 UITransitionView *transition_;
4634 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4635 - (void) reloadData;
4640 @implementation InstallView
4643 [list_ setDataSource:nil];
4644 [list_ setDelegate:nil];
4646 [sections_ release];
4647 [filtered_ release];
4648 [transition_ release];
4650 [accessory_ release];
4654 - (int) numberOfRowsInTable:(UITable *)table {
4655 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4658 - (float) table:(UITable *)table heightForRow:(int)row {
4662 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4664 reusing = [[[SectionCell alloc] init] autorelease];
4665 [(SectionCell *)reusing setSection:(editing_ ?
4666 [sections_ objectAtIndex:row] :
4667 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4668 ) editing:editing_];
4672 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4676 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4680 - (void) tableRowSelected:(NSNotification *)notification {
4681 int row = [[notification object] selectedRow];
4692 title = @"All Packages";
4694 section = [filtered_ objectAtIndex:(row - 1)];
4695 name = [section name];
4701 title = @"(No Section)";
4705 PackageTable *table = [[[PackageTable alloc]
4709 filter:@selector(isVisiblyUninstalledInSection:)
4713 [table setDelegate:delegate_];
4715 [book_ pushPage:table];
4718 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4719 if ((self = [super initWithBook:book]) != nil) {
4720 database_ = database;
4722 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4723 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4725 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4726 [self addSubview:transition_];
4728 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4729 [transition_ transition:0 toView:list_];
4731 UITableColumn *column = [[[UITableColumn alloc]
4732 initWithTitle:@"Name"
4734 width:[self frame].size.width
4737 [list_ setDataSource:self];
4738 [list_ setSeparatorStyle:1];
4739 [list_ addTableColumn:column];
4740 [list_ setDelegate:self];
4741 [list_ setReusesTableCells:YES];
4747 - (void) reloadData {
4748 NSArray *packages = [database_ packages];
4750 [sections_ removeAllObjects];
4751 [filtered_ removeAllObjects];
4753 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4754 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4756 for (size_t i(0); i != [packages count]; ++i) {
4757 Package *package([packages objectAtIndex:i]);
4758 NSString *name([package section]);
4761 Section *section([sections objectForKey:name]);
4762 if (section == nil) {
4763 section = [[[Section alloc] initWithName:name] autorelease];
4764 [sections setObject:section forKey:name];
4768 if ([package valid] && [package installed] == nil && [package visible])
4769 [filtered addObject:package];
4772 [sections_ addObjectsFromArray:[sections allValues]];
4773 [sections_ sortUsingSelector:@selector(compareByName:)];
4775 [filtered sortUsingSelector:@selector(compareBySection:)];
4777 Section *section = nil;
4778 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4779 Package *package = [filtered objectAtIndex:offset];
4780 NSString *name = [package section];
4782 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4783 section = name == nil ?
4784 [[[Section alloc] initWithName:nil] autorelease] :
4785 [sections objectForKey:name];
4786 [filtered_ addObject:section];
4789 [section addToCount];
4795 - (void) resetView {
4797 [self _rightButtonClicked];
4800 - (void) resetViewAnimated:(BOOL)animated {
4801 [list_ resetViewAnimated:animated];
4804 - (void) _rightButtonClicked {
4805 if ((editing_ = !editing_))
4808 [delegate_ updateData];
4811 [book_ setTitle:[self title] forPage:self];
4812 [book_ reloadButtonsForPage:self];
4815 - (NSString *) title {
4816 return editing_ ? @"Section Visibility" : @"Install by Section";
4819 - (NSString *) backButtonTitle {
4823 - (NSString *) rightButtonTitle {
4824 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4827 - (UIView *) accessoryView {
4833 /* Changes View {{{ */
4834 @interface ChangesView : RVPage {
4835 _transient Database *database_;
4836 NSMutableArray *packages_;
4837 NSMutableArray *sections_;
4838 UISectionList *list_;
4842 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4843 - (void) reloadData;
4847 @implementation ChangesView
4850 [[list_ table] setDelegate:nil];
4851 [list_ setDataSource:nil];
4853 [packages_ release];
4854 [sections_ release];
4859 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4860 return [sections_ count];
4863 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4864 return [[sections_ objectAtIndex:section] name];
4867 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4868 return [[sections_ objectAtIndex:section] row];
4871 - (int) numberOfRowsInTable:(UITable *)table {
4872 return [packages_ count];
4875 - (float) table:(UITable *)table heightForRow:(int)row {
4876 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4879 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4881 reusing = [[[PackageCell alloc] init] autorelease];
4882 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4886 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4890 - (void) tableRowSelected:(NSNotification *)notification {
4891 int row = [[notification object] selectedRow];
4894 Package *package = [packages_ objectAtIndex:row];
4895 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4896 [view setDelegate:delegate_];
4897 [view setPackage:package];
4898 [book_ pushPage:view];
4901 - (void) _leftButtonClicked {
4902 [(CYBook *)book_ update];
4903 [self reloadButtons];
4906 - (void) _rightButtonClicked {
4907 [delegate_ distUpgrade];
4910 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4911 if ((self = [super initWithBook:book]) != nil) {
4912 database_ = database;
4914 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4915 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4917 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4918 [self addSubview:list_];
4920 [list_ setShouldHideHeaderInShortLists:NO];
4921 [list_ setDataSource:self];
4922 //[list_ setSectionListStyle:1];
4924 UITableColumn *column = [[[UITableColumn alloc]
4925 initWithTitle:@"Name"
4927 width:[self frame].size.width
4930 UITable *table = [list_ table];
4931 [table setSeparatorStyle:1];
4932 [table addTableColumn:column];
4933 [table setDelegate:self];
4934 [table setReusesTableCells:YES];
4940 - (void) reloadData {
4941 NSArray *packages = [database_ packages];
4943 [packages_ removeAllObjects];
4944 [sections_ removeAllObjects];
4946 for (size_t i(0); i != [packages count]; ++i) {
4947 Package *package([packages objectAtIndex:i]);
4950 [package installed] == nil && [package valid] && [package visible] ||
4951 [package upgradableAndEssential:NO]
4953 [packages_ addObject:package];
4956 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4958 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4959 Section *section = nil;
4962 bool unseens = false;
4964 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4966 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4967 Package *package = [packages_ objectAtIndex:offset];
4969 if ([package upgradableAndEssential:YES]) {
4971 [upgradable addToCount];
4974 NSDate *seen = [package seen];
4979 name = [@"n/a ?" retain];
4981 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4984 if (section == nil || ![[section name] isEqualToString:name]) {
4985 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4986 [sections_ addObject:section];
4990 [section addToCount];
4994 CFRelease(formatter);
4997 Section *last = [sections_ lastObject];
4998 size_t count = [last count];
4999 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5000 [sections_ removeLastObject];
5004 [sections_ insertObject:upgradable atIndex:0];
5007 [self reloadButtons];
5010 - (void) resetViewAnimated:(BOOL)animated {
5011 [list_ resetViewAnimated:animated];
5014 - (NSString *) leftButtonTitle {
5015 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5018 - (NSString *) rightButtonTitle {
5019 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5022 - (NSString *) title {
5028 /* Search View {{{ */
5029 @protocol SearchViewDelegate
5030 - (void) showKeyboard:(BOOL)show;
5033 @interface SearchView : RVPage {
5035 UISearchField *field_;
5036 UITransitionView *transition_;
5037 PackageTable *table_;
5038 UIPreferencesTable *advanced_;
5044 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5045 - (void) reloadData;
5049 @implementation SearchView
5053 [[field_ textTraits] setEditingDelegate:nil];
5055 [field_ setDelegate:nil];
5057 [accessory_ release];
5059 [transition_ release];
5061 [advanced_ release];
5066 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5070 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5072 case 0: return @"Advanced Search (Coming Soon!)";
5074 default: _assert(false);
5078 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5082 default: _assert(false);
5086 - (void) _showKeyboard:(BOOL)show {
5087 CGSize keysize = [UIKeyboard defaultSize];
5088 CGRect keydown = [book_ pageBounds];
5089 CGRect keyup = keydown;
5090 keyup.size.height -= keysize.height - ButtonBarHeight_;
5092 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5094 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5095 [animation setSignificantRectFields:8];
5098 [animation setStartFrame:keydown];
5099 [animation setEndFrame:keyup];
5101 [animation setStartFrame:keyup];
5102 [animation setEndFrame:keydown];
5105 UIAnimator *animator = [UIAnimator sharedAnimator];
5108 addAnimations:[NSArray arrayWithObjects:animation, nil]
5109 withDuration:(KeyboardTime_ - delay)
5114 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5117 [delegate_ showKeyboard:show];
5121 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5122 [self _showKeyboard:YES];
5125 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5126 [self _showKeyboard:NO];
5129 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5131 NSString *text([field_ text]);
5132 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5138 - (void) textFieldClearButtonPressed:(UITextField *)field {
5142 - (void) keyboardInputShouldDelete:(id)input {
5146 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5147 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5151 [field_ resignFirstResponder];
5156 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5157 if ((self = [super initWithBook:book]) != nil) {
5158 CGRect pageBounds = [book_ pageBounds];
5160 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5161 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5162 [self addSubview:pinstripe];*/
5164 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5165 [self addSubview:transition_];
5167 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5169 [advanced_ setReusesTableCells:YES];
5170 [advanced_ setDataSource:self];
5171 [advanced_ reloadData];
5173 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5174 CGColor dimmed(space_, 0, 0, 0, 0.5);
5175 [dimmed_ setBackgroundColor:dimmed];
5177 table_ = [[PackageTable alloc]
5181 filter:@selector(isVisiblySearchedForBy:)
5185 [table_ setShouldHideHeaderInShortLists:NO];
5186 [transition_ transition:0 toView:table_];
5195 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5202 [self bounds].size.width - area.origin.x - 18;
5204 area.size.height = [UISearchField defaultHeight];
5206 field_ = [[UISearchField alloc] initWithFrame:area];
5208 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
5209 [field_ setFont:font];
5212 [field_ setPlaceholder:@"Package Names & Descriptions"];
5213 [field_ setDelegate:self];
5216 [field_ setPaddingTop:3];
5218 [field_ setPaddingTop:5];
5221 UITextTraits *traits = [field_ textTraits];
5222 [traits setAutoCapsType:0];
5223 [traits setAutoCorrectionType:1];
5224 [traits setReturnKeyType:6];
5227 [traits setEditingDelegate:self];
5230 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5232 accessory_ = [[UIView alloc] initWithFrame:accrect];
5233 [accessory_ addSubview:field_];
5235 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5236 [configure setShowPressFeedback:YES];
5237 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5238 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5239 [accessory_ addSubview:configure];*/
5245 LKAnimation *animation = [LKTransition animation];
5246 [animation setType:@"oglFlip"];
5247 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5248 [animation setFillMode:@"extended"];
5249 [animation setTransitionFlags:3];
5250 [animation setDuration:10];
5251 [animation setSpeed:0.35];
5252 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5253 [[transition_ _layer] addAnimation:animation forKey:0];
5254 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5255 flipped_ = !flipped_;
5259 - (void) configurePushed {
5260 [field_ resignFirstResponder];
5264 - (void) resetViewAnimated:(BOOL)animated {
5267 [table_ resetViewAnimated:animated];
5270 - (void) reloadData {
5273 [table_ setObject:[field_ text]];
5274 [table_ reloadData];
5275 [table_ resetCursor];
5278 - (UIView *) accessoryView {
5282 - (NSString *) title {
5286 - (NSString *) backButtonTitle {
5290 - (void) setDelegate:(id)delegate {
5291 [table_ setDelegate:delegate];
5292 [super setDelegate:delegate];
5298 @implementation CYBook
5302 [indicator_ release];
5304 [progress_ release];
5308 - (NSString *) getTitleForPage:(RVPage *)page {
5309 return Simplify([super getTitleForPage:page]);
5317 [navbar_ setPrompt:@""];
5318 [navbar_ addSubview:overlay_];
5319 [indicator_ startAnimation];
5320 [prompt_ setText:@"Updating Database..."];
5321 [progress_ setProgress:0];
5326 detachNewThreadSelector:@selector(_update)
5335 [overlay_ removeFromSuperview];
5336 [indicator_ stopAnimation];
5337 [delegate_ reloadData];
5339 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5342 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5343 if ((self = [super initWithFrame:frame]) != nil) {
5344 database_ = database;
5347 [navbar_ setBarStyle:1];
5349 CGRect ovrrect = [navbar_ bounds];
5350 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5352 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5354 UIProgressIndicatorStyle style = Advanced_ ?
5355 kUIProgressIndicatorStyleSmallWhite :
5356 kUIProgressIndicatorStyleSmallBlack;
5358 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5359 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5360 CGRect indrect = {{indoffset, indoffset}, indsize};
5362 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5363 [indicator_ setStyle:style];
5364 [overlay_ addSubview:indicator_];
5366 CGSize prmsize = {200, indsize.width + 4};
5369 indoffset * 2 + indsize.width,
5373 (ovrrect.size.height - prmsize.height) / 2
5376 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
5378 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5380 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
5381 [prompt_ setBackgroundColor:Clear_];
5382 [prompt_ setFont:font];
5386 [overlay_ addSubview:prompt_];
5388 CGSize prgsize = {75, 100};
5391 ovrrect.size.width - prgsize.width - 10,
5392 (ovrrect.size.height - prgsize.height) / 2
5395 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5396 [progress_ setStyle:0];
5397 [overlay_ addSubview:progress_];
5402 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5405 status.setDelegate(self);
5407 [database_ updateWithStatus:status];
5410 performSelectorOnMainThread:@selector(_update_)
5418 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5419 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5422 - (void) setProgressTitle:(NSString *)title {
5424 performSelectorOnMainThread:@selector(_setProgressTitle:)
5430 - (void) setProgressPercent:(float)percent {
5433 - (void) addProgressOutput:(NSString *)output {
5435 performSelectorOnMainThread:@selector(_addProgressOutput:)
5441 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5445 - (void) _setProgressTitle:(NSString *)title {
5446 [prompt_ setText:[title stringByAppendingString:@"..."]];
5449 - (void) _addProgressOutput:(NSString *)output {
5454 @interface Cydia : UIApplication <
5455 ConfirmationViewDelegate,
5456 ProgressViewDelegate,
5465 UIButtonBar *buttonbar_;
5467 ConfirmationView *confirm_;
5469 NSMutableArray *essential_;
5470 NSMutableArray *broken_;
5472 Database *database_;
5473 ProgressView *progress_;
5477 UIKeyboard *keyboard_;
5478 UIProgressHUD *hud_;
5480 InstallView *install_;
5481 ChangesView *changes_;
5482 ManageView *manage_;
5483 SearchView *search_;
5488 @implementation Cydia
5491 if ([broken_ count] != 0) {
5492 int count = [broken_ count];
5494 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5495 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5496 buttons:[NSArray arrayWithObjects:
5498 @"Ignore (Temporary)",
5500 defaultButtonIndex:0
5505 [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."];
5506 [sheet popupAlertAnimated:YES];
5507 } else if (!Ignored_ && [essential_ count] != 0) {
5508 int count = [essential_ count];
5510 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5511 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5512 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5513 defaultButtonIndex:0
5518 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5519 [sheet popupAlertAnimated:YES];
5523 - (void) _reloadData {
5524 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5525 [hud setText:@"Reloading Data"];
5526 [overlay_ addSubview:hud];
5529 [database_ reloadData];
5533 [essential_ removeAllObjects];
5534 [broken_ removeAllObjects];
5536 NSArray *packages = [database_ packages];
5537 for (int i(0), e([packages count]); i != e; ++i) {
5538 Package *package = [packages objectAtIndex:i];
5540 [broken_ addObject:package];
5541 if ([package upgradableAndEssential:NO]) {
5542 if ([package essential])
5543 [essential_ addObject:package];
5549 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5550 [buttonbar_ setBadgeValue:badge forButton:3];
5551 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5552 [buttonbar_ setBadgeAnimated:YES forButton:3];
5553 [self setApplicationBadge:badge];
5555 [buttonbar_ setBadgeValue:nil forButton:3];
5556 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5557 [buttonbar_ setBadgeAnimated:NO forButton:3];
5558 [self removeApplicationBadge];
5563 /*if ([packages count] == 0);
5572 [hud removeFromSuperview];*/
5575 - (void) _saveConfig {
5577 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5582 - (void) updateData {
5585 /* XXX: this is just stupid */
5587 [install_ reloadData];
5589 [changes_ reloadData];
5591 [search_ reloadData];
5601 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5602 _assert(file != NULL);
5604 NSArray *keys = [Sources_ allKeys];
5606 for (int i(0), e([keys count]); i != e; ++i) {
5607 NSString *key = [keys objectAtIndex:i];
5608 NSDictionary *source = [Sources_ objectForKey:key];
5610 fprintf(file, "%s %s %s\n",
5611 [[source objectForKey:@"Type"] UTF8String],
5612 [[source objectForKey:@"URI"] UTF8String],
5613 [[source objectForKey:@"Distribution"] UTF8String]
5622 detachNewThreadSelector:@selector(update_)
5625 title:@"Updating Sources..."
5629 - (void) reloadData {
5630 @synchronized (self) {
5631 if (confirm_ == nil)
5637 pkgProblemResolver *resolver = [database_ resolver];
5639 resolver->InstallProtect();
5640 if (!resolver->Resolve(true))
5645 [database_ prepare];
5647 if ([database_ cache]->BrokenCount() == 0)
5648 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5650 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5651 NSArray *packages = [database_ packages];
5653 for (size_t i(0); i != [packages count]; ++i) {
5654 Package *package = [packages objectAtIndex:i];
5655 if ([package broken])
5656 [broken addObject:[package name]];
5659 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5660 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5661 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5662 defaultButtonIndex:0
5667 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5668 [sheet popupAlertAnimated:YES];
5674 - (void) installPackage:(Package *)package {
5675 @synchronized (self) {
5682 - (void) removePackage:(Package *)package {
5683 @synchronized (self) {
5690 - (void) distUpgrade {
5691 @synchronized (self) {
5692 [database_ upgrade];
5698 @synchronized (self) {
5706 [overlay_ removeFromSuperview];
5710 detachNewThreadSelector:@selector(perform)
5717 - (void) bootstrap_ {
5719 [database_ upgrade];
5720 [database_ prepare];
5721 [database_ perform];
5724 - (void) bootstrap {
5726 detachNewThreadSelector:@selector(bootstrap_)
5729 title:@"Bootstrap Install..."
5733 - (void) progressViewIsComplete:(ProgressView *)progress {
5734 @synchronized (self) {
5737 if (confirm_ != nil) {
5738 [underlay_ addSubview:overlay_];
5739 [confirm_ removeFromSuperview];
5746 - (void) setPage:(RVPage *)page {
5747 [page resetViewAnimated:NO];
5748 [page setDelegate:self];
5749 [book_ setPage:page];
5752 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5753 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5754 [browser loadURL:url];
5758 - (void) _setHomePage {
5759 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5762 - (void) buttonBarItemTapped:(id)sender {
5763 unsigned tag = [sender tag];
5765 [book_ resetViewAnimated:YES];
5767 } else if (tag_ == 2 && tag != 2)
5768 [install_ resetView];
5771 case 1: [self _setHomePage]; break;
5773 case 2: [self setPage:install_]; break;
5774 case 3: [self setPage:changes_]; break;
5775 case 4: [self setPage:manage_]; break;
5776 case 5: [self setPage:search_]; break;
5778 default: _assert(false);
5784 - (void) fixSpringBoard {
5785 pid_t pid = ExecFork();
5789 if (pid_t child = fork()) {
5790 waitpid(child, NULL, 0);
5792 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5793 perror("launchctl unload");
5797 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5798 perror("launchctl load");
5803 - (void) applicationWillSuspend {
5808 [self fixSpringBoard];
5812 [super applicationWillSuspend];
5815 - (void) askForSettings {
5816 UIAlertSheet *role = [[[UIAlertSheet alloc]
5817 initWithTitle:@"Who Are You?"
5818 buttons:[NSArray arrayWithObjects:
5819 @"User (Graphical Only)",
5820 @"Hacker (+ Command Line)",
5821 @"Developer (No Filters)",
5823 defaultButtonIndex:-1
5828 [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."];
5829 [role popupAlertAnimated:YES];
5834 [self setStatusBarShowsProgress:NO];
5837 [hud_ removeFromSuperview];
5841 pid_t pid = ExecFork();
5843 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5844 perror("launchctl stop");
5851 [self askForSettings];
5855 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5857 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5858 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5859 0, 0, screenrect.size.width, screenrect.size.height - 48
5860 ) database:database_];
5862 [book_ setDelegate:self];
5864 [overlay_ addSubview:book_];
5866 NSArray *buttonitems = [NSArray arrayWithObjects:
5867 [NSDictionary dictionaryWithObjectsAndKeys:
5868 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5869 @"home-up.png", kUIButtonBarButtonInfo,
5870 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5871 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5872 self, kUIButtonBarButtonTarget,
5873 @"Home", kUIButtonBarButtonTitle,
5874 @"0", kUIButtonBarButtonType,
5877 [NSDictionary dictionaryWithObjectsAndKeys:
5878 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5879 @"install-up.png", kUIButtonBarButtonInfo,
5880 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5881 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5882 self, kUIButtonBarButtonTarget,
5883 @"Sections", kUIButtonBarButtonTitle,
5884 @"0", kUIButtonBarButtonType,
5887 [NSDictionary dictionaryWithObjectsAndKeys:
5888 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5889 @"changes-up.png", kUIButtonBarButtonInfo,
5890 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5891 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5892 self, kUIButtonBarButtonTarget,
5893 @"Changes", kUIButtonBarButtonTitle,
5894 @"0", kUIButtonBarButtonType,
5897 [NSDictionary dictionaryWithObjectsAndKeys:
5898 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5899 @"manage-up.png", kUIButtonBarButtonInfo,
5900 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5901 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5902 self, kUIButtonBarButtonTarget,
5903 @"Manage", kUIButtonBarButtonTitle,
5904 @"0", kUIButtonBarButtonType,
5907 [NSDictionary dictionaryWithObjectsAndKeys:
5908 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5909 @"search-up.png", kUIButtonBarButtonInfo,
5910 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5911 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5912 self, kUIButtonBarButtonTarget,
5913 @"Search", kUIButtonBarButtonTitle,
5914 @"0", kUIButtonBarButtonType,
5918 buttonbar_ = [[UIButtonBar alloc]
5920 withFrame:CGRectMake(
5921 0, screenrect.size.height - ButtonBarHeight_,
5922 screenrect.size.width, ButtonBarHeight_
5924 withItemList:buttonitems
5927 [buttonbar_ setDelegate:self];
5928 [buttonbar_ setBarStyle:1];
5929 [buttonbar_ setButtonBarTrackingMode:2];
5931 int buttons[5] = {1, 2, 3, 4, 5};
5932 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5933 [buttonbar_ showButtonGroup:0 withDuration:0];
5935 for (int i = 0; i != 5; ++i)
5936 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5937 i * 64 + 2, 1, 60, ButtonBarHeight_
5940 [buttonbar_ showSelectionForButton:1];
5941 [overlay_ addSubview:buttonbar_];
5943 [UIKeyboard initImplementationNow];
5944 CGSize keysize = [UIKeyboard defaultSize];
5945 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5946 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5947 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5948 [overlay_ addSubview:keyboard_];
5950 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5951 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5952 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5954 manage_ = (ManageView *) [[self
5955 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5956 withClass:[ManageView class]
5960 [underlay_ addSubview:overlay_];
5967 [self _setHomePage];
5970 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5971 NSString *context = [sheet context];
5972 if ([context isEqualToString:@"fixhalf"])
5975 @synchronized (self) {
5976 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5977 Package *broken = [broken_ objectAtIndex:i];
5980 NSString *id = [broken id];
5981 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5982 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5983 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5984 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5993 [broken_ removeAllObjects];
6000 else if ([context isEqualToString:@"role"]) {
6002 case 1: Role_ = @"User"; break;
6003 case 2: Role_ = @"Hacker"; break;
6004 case 3: Role_ = @"Developer"; break;
6011 bool reset = Settings_ != nil;
6013 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6017 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6025 } else if ([context isEqualToString:@"upgrade"])
6028 @synchronized (self) {
6029 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6030 Package *essential = [essential_ objectAtIndex:i];
6031 [essential install];
6050 - (void) reorganize {
6051 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6052 system("/usr/libexec/cydia/free.sh");
6053 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6057 - (void) applicationSuspend:(__GSEvent *)event {
6058 if (hud_ == nil && ![progress_ isRunning])
6059 [super applicationSuspend:event];
6062 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6064 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6067 - (void) _setSuspended:(BOOL)value {
6069 [super _setSuspended:value];
6072 - (UIProgressHUD *) addProgressHUD {
6073 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6075 [underlay_ addSubview:hud];
6079 - (void) applicationDidFinishLaunching:(id)unused {
6080 Font12_ = [[UIFont systemFontOfSize:12] retain];
6081 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6082 Font14_ = [[UIFont systemFontOfSize:14] retain];
6083 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6084 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6086 _assert(pkgInitConfig(*_config));
6087 _assert(pkgInitSystem(*_config, _system));
6092 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6093 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6095 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6096 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6098 [window_ orderFront:self];
6099 [window_ makeKey:self];
6100 [window_ _setHidden:NO];
6102 database_ = [[Database alloc] init];
6103 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6104 [database_ setDelegate:progress_];
6105 [window_ setContentView:progress_];
6107 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6108 [progress_ setContentView:underlay_];
6110 [progress_ resetView];
6113 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6114 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6115 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6116 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6117 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6118 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
6120 [self setIdleTimerDisabled:YES];
6122 hud_ = [self addProgressHUD];
6123 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6125 [self setStatusBarShowsProgress:YES];
6128 detachNewThreadSelector:@selector(reorganize)
6136 - (void) showKeyboard:(BOOL)show {
6137 CGSize keysize = [UIKeyboard defaultSize];
6138 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6139 CGRect keyup = keydown;
6140 keyup.origin.y -= keysize.height;
6142 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6143 [animation setSignificantRectFields:2];
6146 [animation setStartFrame:keydown];
6147 [animation setEndFrame:keyup];
6148 [keyboard_ activate];
6150 [animation setStartFrame:keyup];
6151 [animation setEndFrame:keydown];
6152 [keyboard_ deactivate];
6155 [[UIAnimator sharedAnimator]
6156 addAnimations:[NSArray arrayWithObjects:animation, nil]
6157 withDuration:KeyboardTime_
6162 - (void) slideUp:(UIAlertSheet *)alert {
6164 [alert presentSheetFromButtonBar:buttonbar_];
6166 [alert presentSheetInView:overlay_];
6171 void AddPreferences(NSString *plist) {
6172 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6174 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6175 _assert(settings != NULL);
6176 NSMutableArray *items = [settings objectForKey:@"items"];
6180 for (size_t i(0); i != [items count]; ++i) {
6181 NSMutableDictionary *item([items objectAtIndex:i]);
6182 NSString *label = [item objectForKey:@"label"];
6183 if (label != nil && [label isEqualToString:@"Cydia"]) {
6190 for (size_t i(0); i != [items count]; ++i) {
6191 NSDictionary *item([items objectAtIndex:i]);
6192 NSString *label = [item objectForKey:@"label"];
6193 if (label != nil && [label isEqualToString:@"General"]) {
6194 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6195 @"CydiaSettings", @"bundle",
6196 @"PSLinkCell", @"cell",
6197 [NSNumber numberWithBool:YES], @"hasIcon",
6198 [NSNumber numberWithBool:YES], @"isController",
6200 nil] atIndex:(i + 1)];
6206 _assert([settings writeToFile:plist atomically:YES] == YES);
6213 id Alloc_(id self, SEL selector) {
6214 id object = alloc_(self, selector);
6215 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6220 id Dealloc_(id self, SEL selector) {
6221 id object = dealloc_(self, selector);
6222 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6226 int main(int argc, char *argv[]) {
6227 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6229 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6231 Home_ = NSHomeDirectory();
6234 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6235 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6236 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6237 Sounds_Keyboard_ = [keyboard boolValue];
6243 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6244 _assert(errno == ENOENT);
6245 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6246 _assert(errno == ENOENT);
6248 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6249 alloc_ = alloc->method_imp;
6250 alloc->method_imp = (IMP) &Alloc_;*/
6252 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6253 dealloc_ = dealloc->method_imp;
6254 dealloc->method_imp = (IMP) &Dealloc_;*/
6256 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6257 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6258 Firmware_ = strdup([prover UTF8String]);
6259 NSArray *versions = [prover componentsSeparatedByString:@"."];
6260 int count = [versions count];
6261 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6262 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6263 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6268 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6269 char *machine = new char[size];
6270 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6273 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6274 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6275 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6276 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6280 IOObjectRelease(service);
6283 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6284 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6286 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6287 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6289 Settings_ = [Metadata_ objectForKey:@"Settings"];
6291 Packages_ = [Metadata_ objectForKey:@"Packages"];
6292 Sections_ = [Metadata_ objectForKey:@"Sections"];
6293 Sources_ = [Metadata_ objectForKey:@"Sources"];
6296 if (Settings_ != nil)
6297 Role_ = [Settings_ objectForKey:@"Role"];
6299 if (Packages_ == nil) {
6300 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6301 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6304 if (Sections_ == nil) {
6305 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6306 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6309 if (Sources_ == nil) {
6310 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6311 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6314 if (access("/User", F_OK) != 0)
6315 system("/usr/libexec/cydia/firmware.sh");
6317 Locale_ = CFLocaleCopyCurrent();
6318 space_ = CGColorSpaceCreateDeviceRGB();
6320 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6321 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6322 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6323 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6324 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6325 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6326 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6328 SectionMap_ = [NSMutableDictionary dictionaryWithCapacity:16]; {
6329 std::ifstream fin([[[NSBundle mainBundle] pathForResource:@"sections" ofType:@"txt"] UTF8String]);
6331 while (std::getline(fin, line)) {
6332 size_t space = line.find_first_of(' ');
6333 if (space == std::string::npos)
6336 setObject:[NSString stringWithUTF8String:line.substr(space + 1).c_str()]
6337 forKey:[NSString stringWithUTF8String:line.substr(0, space).c_str()]
6342 int value = UIApplicationMain(argc, argv, [Cydia class]);
6344 CGColorSpaceRelease(space_);