]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Porting, webkit stability.
[cydia.git] / Cydia.mm
1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
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
17 * distribution.
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.
21 *
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.
36 */
37
38 /* #include Directives {{{ */
39 #include <objc/objc.h>
40 #include <objc/runtime.h>
41
42 #include <CoreGraphics/CoreGraphics.h>
43 #include <GraphicsServices/GraphicsServices.h>
44 #include <Foundation/Foundation.h>
45
46 #include <WebCore/DOMHTML.h>
47 #import <QuartzCore/CALayer.h>
48
49 #import <UIKit/UIKit.h>
50
51 // XXX: remove
52 #import <UIKit/UIActionSheet-Private.h>
53 #import <UIKit/UIControl-UIControlPrivate.h>
54 #import <UIKit/UIImage-UIImageDeprecated.h>
55 #import <UIKit/UIImage-UIImagePrivate.h>
56 #import <UIKit/UINavigationBar-Static.h>
57 #import <UIKit/UIProgressHUD-Deprecated.h>
58 #import <UIKit/UIToolbar-UIButtonBarPrivate.h>
59 #import <UIKit/UIView-Deprecated.h>
60 #import <UIKit/UIWindow-Static.h>
61
62 // XXX: remove
63 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
64
65 #include <WebKit/WebFrame.h>
66 #include <WebKit/WebView.h>
67
68 #include <sstream>
69 #include <string>
70
71 #include <ext/stdio_filebuf.h>
72
73 #include <apt-pkg/acquire.h>
74 #include <apt-pkg/acquire-item.h>
75 #include <apt-pkg/algorithms.h>
76 #include <apt-pkg/cachefile.h>
77 #include <apt-pkg/clean.h>
78 #include <apt-pkg/configuration.h>
79 #include <apt-pkg/debmetaindex.h>
80 #include <apt-pkg/error.h>
81 #include <apt-pkg/init.h>
82 #include <apt-pkg/mmap.h>
83 #include <apt-pkg/pkgrecords.h>
84 #include <apt-pkg/sha1.h>
85 #include <apt-pkg/sourcelist.h>
86 #include <apt-pkg/sptr.h>
87
88 #include <sys/sysctl.h>
89 #include <notify.h>
90 #include <dlfcn.h>
91
92 extern "C" {
93 #include <mach-o/nlist.h>
94 }
95
96 #include <cstdio>
97 #include <cstdlib>
98 #include <cstring>
99
100 #include <errno.h>
101 #include <pcre.h>
102
103 #import "BrowserView.h"
104 #import "ResetView.h"
105 #import "UICaboodle.h"
106 /* }}} */
107
108 static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
109
110 @interface WebView (Cydia)
111 - (void) _setLayoutInterval:(float)interval;
112 @end
113
114 /* iPhoneOS 2.0 Compatibility {{{ */
115 #ifdef __OBJC2__
116 @interface UICGColor : NSObject {
117 }
118
119 - (id) initWithCGColor:(CGColorRef)color;
120 @end
121
122 @interface NSObject (iPhoneOS)
123 - (CGColorRef) cgColor;
124 - (CGColorRef) CGColor;
125 - (void) set;
126 @end
127
128 @implementation NSObject (iPhoneOS)
129
130 - (CGColorRef) cgColor {
131 return [self CGColor];
132 }
133
134 - (CGColorRef) CGColor {
135 return (CGColorRef) self;
136 }
137
138 - (void) set {
139 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
140 }
141
142 @end
143
144 @interface UITextView (iPhoneOS)
145 - (void) setTextSize:(float)size;
146 @end
147
148 @implementation UITextView (iPhoneOS)
149
150 - (void) setTextSize:(float)size {
151 [self setFont:[[self font] fontWithSize:size]];
152 }
153
154 @end
155 #endif
156 /* }}} */
157
158 @interface UIApplication (IdleTimer)
159 - (void) setIdleTimerDisabled:(char)arg0;
160 @end
161
162 extern "C" int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
163
164 extern NSString *kUIButtonBarButtonAction;
165 extern NSString *kUIButtonBarButtonInfo;
166 extern NSString *kUIButtonBarButtonInfoOffset;
167 extern NSString *kUIButtonBarButtonSelectedInfo;
168 extern NSString *kUIButtonBarButtonStyle;
169 extern NSString *kUIButtonBarButtonTag;
170 extern NSString *kUIButtonBarButtonTarget;
171 extern NSString *kUIButtonBarButtonTitle;
172 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
173 extern NSString *kUIButtonBarButtonTitleWidth;
174 extern NSString *kUIButtonBarButtonType;
175
176 typedef enum {
177 kUIProgressIndicatorStyleLargeWhite = 0,
178 kUIProgressIndicatorStyleMediumWhite = 1,
179 kUIProgressIndicatorStyleMediumBrown = 2,
180 kUIProgressIndicatorStyleSmallWhite = 3,
181 kUIProgressIndicatorStyleSmallBlack = 4,
182 kUIProgressIndicatorStyleTinyWhite = 5,
183 } UIProgressIndicatorStyle;
184
185 typedef enum {
186 kUIControlEventMouseDown = 1 << 0,
187 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
188 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
189 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
190 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
191 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
192 } UIControlEventMasks;
193
194 @interface NSString (UIKit)
195 - (NSString *) stringByAddingPercentEscapes;
196 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
197 @end
198
199 @interface NSString (Cydia)
200 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
201 - (NSComparisonResult) compareByPath:(NSString *)other;
202 @end
203
204 @implementation NSString (Cydia)
205
206 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
207 char data[length + 1];
208 memcpy(data, bytes, length);
209 data[length] = '\0';
210 return [NSString stringWithUTF8String:data];
211 }
212
213 - (NSComparisonResult) compareByPath:(NSString *)other {
214 NSString *prefix = [self commonPrefixWithString:other options:0];
215 size_t length = [prefix length];
216
217 NSRange lrange = NSMakeRange(length, [self length] - length);
218 NSRange rrange = NSMakeRange(length, [other length] - length);
219
220 lrange = [self rangeOfString:@"/" options:0 range:lrange];
221 rrange = [other rangeOfString:@"/" options:0 range:rrange];
222
223 NSComparisonResult value;
224
225 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
226 value = NSOrderedSame;
227 else if (lrange.location == NSNotFound)
228 value = NSOrderedAscending;
229 else if (rrange.location == NSNotFound)
230 value = NSOrderedDescending;
231 else
232 value = NSOrderedSame;
233
234 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
235 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
236 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
237 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
238
239 NSComparisonResult result = [lpath compare:rpath];
240 return result == NSOrderedSame ? value : result;
241 }
242
243 @end
244
245 /* Perl-Compatible RegEx {{{ */
246 class Pcre {
247 private:
248 pcre *code_;
249 pcre_extra *study_;
250 int capture_;
251 int *matches_;
252 const char *data_;
253
254 public:
255 Pcre(const char *regex) :
256 study_(NULL)
257 {
258 const char *error;
259 int offset;
260 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
261
262 if (code_ == NULL) {
263 fprintf(stderr, "%d:%s\n", offset, error);
264 _assert(false);
265 }
266
267 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
268 matches_ = new int[(capture_ + 1) * 3];
269 }
270
271 ~Pcre() {
272 pcre_free(code_);
273 delete matches_;
274 }
275
276 NSString *operator [](size_t match) {
277 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
278 }
279
280 bool operator ()(NSString *data) {
281 // XXX: length is for characters, not for bytes
282 return operator ()([data UTF8String], [data length]);
283 }
284
285 bool operator ()(const char *data, size_t size) {
286 data_ = data;
287 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
288 }
289 };
290 /* }}} */
291 /* Mime Addresses {{{ */
292 @interface Address : NSObject {
293 NSString *name_;
294 NSString *address_;
295 }
296
297 - (NSString *) name;
298 - (NSString *) address;
299
300 + (Address *) addressWithString:(NSString *)string;
301 - (Address *) initWithString:(NSString *)string;
302 @end
303
304 @implementation Address
305
306 - (void) dealloc {
307 [name_ release];
308 if (address_ != nil)
309 [address_ release];
310 [super dealloc];
311 }
312
313 - (NSString *) name {
314 return name_;
315 }
316
317 - (NSString *) address {
318 return address_;
319 }
320
321 + (Address *) addressWithString:(NSString *)string {
322 return [[[Address alloc] initWithString:string] autorelease];
323 }
324
325 + (NSArray *) _attributeKeys {
326 return [NSArray arrayWithObjects:@"address", @"name", nil];
327 }
328
329 - (NSArray *) attributeKeys {
330 return [[self class] _attributeKeys];
331 }
332
333 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
334 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
335 }
336
337 - (Address *) initWithString:(NSString *)string {
338 if ((self = [super init]) != nil) {
339 const char *data = [string UTF8String];
340 size_t size = [string length];
341
342 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
343
344 if (address_r(data, size)) {
345 name_ = [address_r[1] retain];
346 address_ = [address_r[2] retain];
347 } else {
348 name_ = [[NSString alloc]
349 initWithBytes:data
350 length:size
351 encoding:kCFStringEncodingUTF8
352 ];
353
354 address_ = nil;
355 }
356 } return self;
357 }
358
359 @end
360 /* }}} */
361 /* CoreGraphics Primitives {{{ */
362 class CGColor {
363 private:
364 CGColorRef color_;
365
366 public:
367 CGColor() :
368 color_(NULL)
369 {
370 }
371
372 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
373 color_(NULL)
374 {
375 Set(space, red, green, blue, alpha);
376 }
377
378 void Clear() {
379 if (color_ != NULL)
380 CGColorRelease(color_);
381 }
382
383 ~CGColor() {
384 Clear();
385 }
386
387 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
388 Clear();
389 float color[] = {red, green, blue, alpha};
390 color_ = CGColorCreate(space, color);
391 }
392
393 operator CGColorRef() {
394 return color_;
395 }
396 };
397
398 class GSFont {
399 private:
400 GSFontRef font_;
401
402 public:
403 ~GSFont() {
404 CFRelease(font_);
405 }
406 };
407 /* }}} */
408
409 extern "C" void UISetColor(CGColorRef color);
410
411 /* Random Global Variables {{{ */
412 static const int PulseInterval_ = 50000;
413 static const int ButtonBarHeight_ = 48;
414 static const float KeyboardTime_ = 0.3f;
415 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
416
417 static CGColor Blue_;
418 static CGColor Blueish_;
419 static CGColor Black_;
420 static CGColor Off_;
421 static CGColor Red_;
422 static CGColor White_;
423 static CGColor Gray_;
424
425 static NSString *App_;
426 static NSString *Home_;
427 static BOOL Sounds_Keyboard_;
428
429 static BOOL Advanced_;
430 static BOOL Loaded_;
431 static BOOL Ignored_;
432
433 static UIFont *Font12_;
434 static UIFont *Font12Bold_;
435 static UIFont *Font14_;
436 static UIFont *Font18Bold_;
437 static UIFont *Font22Bold_;
438
439 const char *Firmware_ = NULL;
440 const char *Machine_ = NULL;
441 const char *SerialNumber_ = NULL;
442
443 unsigned Major_;
444 unsigned Minor_;
445 unsigned BugFix_;
446
447 CFLocaleRef Locale_;
448 CGColorSpaceRef space_;
449
450 #define FW_LEAST(major, minor, bugfix) \
451 (major < Major_ || major == Major_ && \
452 (minor < Minor_ || minor == Minor_ && \
453 bugfix <= BugFix_))
454
455 bool bootstrap_;
456 bool reload_;
457
458 static NSDictionary *SectionMap_;
459 static NSMutableDictionary *Metadata_;
460 static _transient NSMutableDictionary *Settings_;
461 static _transient NSString *Role_;
462 static _transient NSMutableDictionary *Packages_;
463 static _transient NSMutableDictionary *Sections_;
464 static _transient NSMutableDictionary *Sources_;
465 static _transient NSMutableArray *Documents_;
466 static bool Changed_;
467 static NSDate *now_;
468
469 NSString *GetLastUpdate() {
470 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
471
472 if (update == nil)
473 return @"Never or Unknown";
474
475 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
476 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
477
478 CFRelease(formatter);
479
480 return [(NSString *) formatted autorelease];
481 }
482 /* }}} */
483 /* Display Helpers {{{ */
484 inline float Interpolate(float begin, float end, float fraction) {
485 return (end - begin) * fraction + begin;
486 }
487
488 NSString *SizeString(double size) {
489 unsigned power = 0;
490 while (size > 1024) {
491 size /= 1024;
492 ++power;
493 }
494
495 static const char *powers_[] = {"B", "kB", "MB", "GB"};
496
497 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
498 }
499
500 NSString *StripVersion(NSString *version) {
501 NSRange colon = [version rangeOfString:@":"];
502 if (colon.location != NSNotFound)
503 version = [version substringFromIndex:(colon.location + 1)];
504 return version;
505 }
506
507 static const float TextViewOffset_ = 22;
508
509 UITextView *GetTextView(NSString *value, float left, bool html) {
510 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
511 [text setEditable:NO];
512 [text setTextSize:16];
513 /*if (html)
514 [text setHTML:value];
515 else*/
516 [text setText:value];
517 [text setEnabled:NO];
518
519 [text setBackgroundColor:[UIColor clearColor]];
520
521 CGRect frame = [text frame];
522 [text setFrame:frame];
523 CGRect rect = [text visibleTextRect];
524 frame.size.height = rect.size.height;
525 [text setFrame:frame];
526
527 return text;
528 }
529
530 NSString *Simplify(NSString *title) {
531 const char *data = [title UTF8String];
532 size_t size = [title length];
533
534 static Pcre square_r("^\\[(.*)\\]$");
535 if (square_r(data, size))
536 return Simplify(square_r[1]);
537
538 static Pcre paren_r("^\\((.*)\\)$");
539 if (paren_r(data, size))
540 return Simplify(paren_r[1]);
541
542 static Pcre title_r("^(.*?) \\(.*\\)$");
543 if (title_r(data, size))
544 return Simplify(title_r[1]);
545
546 return title;
547 }
548 /* }}} */
549
550 bool isSectionVisible(NSString *section) {
551 NSDictionary *metadata = [Sections_ objectForKey:section];
552 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
553 return hidden == nil || ![hidden boolValue];
554 }
555
556 /* Delegate Prototypes {{{ */
557 @class Package;
558 @class Source;
559
560 @interface NSObject (ProgressDelegate)
561 @end
562
563 @implementation NSObject(ProgressDelegate)
564
565 - (void) _setProgressError:(NSArray *)args {
566 [self performSelector:@selector(setProgressError:forPackage:)
567 withObject:[args objectAtIndex:0]
568 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
569 ];
570 }
571
572 @end
573
574 @protocol ProgressDelegate
575 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
576 - (void) setProgressTitle:(NSString *)title;
577 - (void) setProgressPercent:(float)percent;
578 - (void) startProgress;
579 - (void) addProgressOutput:(NSString *)output;
580 - (bool) isCancelling:(size_t)received;
581 @end
582
583 @protocol ConfigurationDelegate
584 - (void) repairWithSelector:(SEL)selector;
585 - (void) setConfigurationData:(NSString *)data;
586 @end
587
588 @protocol CydiaDelegate
589 - (void) installPackage:(Package *)package;
590 - (void) removePackage:(Package *)package;
591 - (void) slideUp:(UIActionSheet *)alert;
592 - (void) distUpgrade;
593 - (void) updateData;
594 - (void) syncData;
595 - (void) askForSettings;
596 - (UIProgressHUD *) addProgressHUD;
597 @end
598 /* }}} */
599
600 /* Status Delegation {{{ */
601 class Status :
602 public pkgAcquireStatus
603 {
604 private:
605 _transient NSObject<ProgressDelegate> *delegate_;
606
607 public:
608 Status() :
609 delegate_(nil)
610 {
611 }
612
613 void setDelegate(id delegate) {
614 delegate_ = delegate;
615 }
616
617 virtual bool MediaChange(std::string media, std::string drive) {
618 return false;
619 }
620
621 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
622 }
623
624 virtual void Fetch(pkgAcquire::ItemDesc &item) {
625 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
626 }
627
628 virtual void Done(pkgAcquire::ItemDesc &item) {
629 }
630
631 virtual void Fail(pkgAcquire::ItemDesc &item) {
632 if (
633 item.Owner->Status == pkgAcquire::Item::StatIdle ||
634 item.Owner->Status == pkgAcquire::Item::StatDone
635 )
636 return;
637
638 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
639 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
640 waitUntilDone:YES
641 ];
642 }
643
644 virtual bool Pulse(pkgAcquire *Owner) {
645 bool value = pkgAcquireStatus::Pulse(Owner);
646
647 float percent(
648 double(CurrentBytes + CurrentItems) /
649 double(TotalBytes + TotalItems)
650 );
651
652 [delegate_ setProgressPercent:percent];
653 return [delegate_ isCancelling:CurrentBytes] ? false : value;
654 }
655
656 virtual void Start() {
657 [delegate_ startProgress];
658 }
659
660 virtual void Stop() {
661 }
662 };
663 /* }}} */
664 /* Progress Delegation {{{ */
665 class Progress :
666 public OpProgress
667 {
668 private:
669 _transient id<ProgressDelegate> delegate_;
670
671 protected:
672 virtual void Update() {
673 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
674 [delegate_ setProgressPercent:(Percent / 100)];
675 }
676
677 public:
678 Progress() :
679 delegate_(nil)
680 {
681 }
682
683 void setDelegate(id delegate) {
684 delegate_ = delegate;
685 }
686
687 virtual void Done() {
688 [delegate_ setProgressPercent:1];
689 }
690 };
691 /* }}} */
692
693 /* Database Interface {{{ */
694 @interface Database : NSObject {
695 pkgCacheFile cache_;
696 pkgDepCache::Policy *policy_;
697 pkgRecords *records_;
698 pkgProblemResolver *resolver_;
699 pkgAcquire *fetcher_;
700 FileFd *lock_;
701 SPtr<pkgPackageManager> manager_;
702 pkgSourceList *list_;
703
704 NSMutableDictionary *sources_;
705 NSMutableArray *packages_;
706
707 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
708 Status status_;
709 Progress progress_;
710
711 int cydiafd_;
712 int statusfd_;
713 FILE *input_;
714 }
715
716 - (void) _readCydia:(NSNumber *)fd;
717 - (void) _readStatus:(NSNumber *)fd;
718 - (void) _readOutput:(NSNumber *)fd;
719
720 - (FILE *) input;
721
722 - (Package *) packageWithName:(NSString *)name;
723
724 - (Database *) init;
725 - (pkgCacheFile &) cache;
726 - (pkgDepCache::Policy *) policy;
727 - (pkgRecords *) records;
728 - (pkgProblemResolver *) resolver;
729 - (pkgAcquire &) fetcher;
730 - (NSArray *) packages;
731 - (NSArray *) sources;
732 - (void) reloadData;
733
734 - (void) configure;
735 - (void) prepare;
736 - (void) perform;
737 - (void) upgrade;
738 - (void) update;
739
740 - (void) updateWithStatus:(Status &)status;
741
742 - (void) setDelegate:(id)delegate;
743 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
744 @end
745 /* }}} */
746
747 /* Source Class {{{ */
748 @interface Source : NSObject {
749 NSString *description_;
750 NSString *label_;
751 NSString *origin_;
752
753 NSString *uri_;
754 NSString *distribution_;
755 NSString *type_;
756 NSString *version_;
757
758 NSString *defaultIcon_;
759
760 NSDictionary *record_;
761 BOOL trusted_;
762 }
763
764 - (Source *) initWithMetaIndex:(metaIndex *)index;
765
766 - (NSComparisonResult) compareByNameAndType:(Source *)source;
767
768 - (NSDictionary *) record;
769 - (BOOL) trusted;
770
771 - (NSString *) uri;
772 - (NSString *) distribution;
773 - (NSString *) type;
774 - (NSString *) key;
775 - (NSString *) host;
776
777 - (NSString *) name;
778 - (NSString *) description;
779 - (NSString *) label;
780 - (NSString *) origin;
781 - (NSString *) version;
782
783 - (NSString *) defaultIcon;
784
785 @end
786
787 @implementation Source
788
789 - (void) dealloc {
790 [uri_ release];
791 [distribution_ release];
792 [type_ release];
793
794 if (description_ != nil)
795 [description_ release];
796 if (label_ != nil)
797 [label_ release];
798 if (origin_ != nil)
799 [origin_ release];
800 if (version_ != nil)
801 [version_ release];
802 if (defaultIcon_ != nil)
803 [defaultIcon_ release];
804 if (record_ != nil)
805 [record_ release];
806
807 [super dealloc];
808 }
809
810 + (NSArray *) _attributeKeys {
811 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
812 }
813
814 - (NSArray *) attributeKeys {
815 return [[self class] _attributeKeys];
816 }
817
818 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
819 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
820 }
821
822 - (Source *) initWithMetaIndex:(metaIndex *)index {
823 if ((self = [super init]) != nil) {
824 trusted_ = index->IsTrusted();
825
826 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
827 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
828 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
829
830 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
831 if (dindex != NULL) {
832 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
833 std::string line;
834 while (std::getline(release, line)) {
835 std::string::size_type colon(line.find(':'));
836 if (colon == std::string::npos)
837 continue;
838
839 std::string name(line.substr(0, colon));
840 std::string value(line.substr(colon + 1));
841 while (!value.empty() && value[0] == ' ')
842 value = value.substr(1);
843
844 if (name == "Default-Icon")
845 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
846 else if (name == "Description")
847 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
848 else if (name == "Label")
849 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
850 else if (name == "Origin")
851 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
852 else if (name == "Version")
853 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
854 }
855 }
856
857 record_ = [Sources_ objectForKey:[self key]];
858 if (record_ != nil)
859 record_ = [record_ retain];
860 } return self;
861 }
862
863 - (NSComparisonResult) compareByNameAndType:(Source *)source {
864 NSDictionary *lhr = [self record];
865 NSDictionary *rhr = [source record];
866
867 if (lhr != rhr)
868 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
869
870 NSString *lhs = [self name];
871 NSString *rhs = [source name];
872
873 if ([lhs length] != 0 && [rhs length] != 0) {
874 unichar lhc = [lhs characterAtIndex:0];
875 unichar rhc = [rhs characterAtIndex:0];
876
877 if (isalpha(lhc) && !isalpha(rhc))
878 return NSOrderedAscending;
879 else if (!isalpha(lhc) && isalpha(rhc))
880 return NSOrderedDescending;
881 }
882
883 return [lhs compare:rhs options:CompareOptions_];
884 }
885
886 - (NSDictionary *) record {
887 return record_;
888 }
889
890 - (BOOL) trusted {
891 return trusted_;
892 }
893
894 - (NSString *) uri {
895 return uri_;
896 }
897
898 - (NSString *) distribution {
899 return distribution_;
900 }
901
902 - (NSString *) type {
903 return type_;
904 }
905
906 - (NSString *) key {
907 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
908 }
909
910 - (NSString *) host {
911 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
912 }
913
914 - (NSString *) name {
915 return origin_ == nil ? [self host] : origin_;
916 }
917
918 - (NSString *) description {
919 return description_;
920 }
921
922 - (NSString *) label {
923 return label_ == nil ? [self host] : label_;
924 }
925
926 - (NSString *) origin {
927 return origin_;
928 }
929
930 - (NSString *) version {
931 return version_;
932 }
933
934 - (NSString *) defaultIcon {
935 return defaultIcon_;
936 }
937
938 @end
939 /* }}} */
940 /* Relationship Class {{{ */
941 @interface Relationship : NSObject {
942 NSString *type_;
943 NSString *id_;
944 }
945
946 - (NSString *) type;
947 - (NSString *) id;
948 - (NSString *) name;
949
950 @end
951
952 @implementation Relationship
953
954 - (void) dealloc {
955 [type_ release];
956 [id_ release];
957 [super dealloc];
958 }
959
960 - (NSString *) type {
961 return type_;
962 }
963
964 - (NSString *) id {
965 return id_;
966 }
967
968 - (NSString *) name {
969 _assert(false);
970 return nil;
971 }
972
973 @end
974 /* }}} */
975 /* Package Class {{{ */
976 NSString *Scour(const char *field, const char *begin, const char *end) {
977 size_t i(0), l(strlen(field));
978
979 for (;;) {
980 const char *name = begin + i;
981 const char *colon = name + l;
982 const char *value = colon + 1;
983
984 if (
985 value < end &&
986 *colon == ':' &&
987 memcmp(name, field, l) == 0
988 ) {
989 while (value != end && value[0] == ' ')
990 ++value;
991 const char *line = std::find(value, end, '\n');
992 while (line != value && line[-1] == ' ')
993 --line;
994
995 return [NSString stringWithUTF8Bytes:value length:(line - value)];
996 } else {
997 begin = std::find(begin, end, '\n');
998 if (begin == end)
999 return nil;
1000 ++begin;
1001 }
1002 }
1003 }
1004
1005 @interface Package : NSObject {
1006 pkgCache::PkgIterator iterator_;
1007 _transient Database *database_;
1008 pkgCache::VerIterator version_;
1009 pkgCache::VerFileIterator file_;
1010
1011 Source *source_;
1012 bool cached_;
1013
1014 NSString *latest_;
1015 NSString *installed_;
1016
1017 NSString *id_;
1018 NSString *name_;
1019 NSString *tagline_;
1020 NSString *icon_;
1021 NSString *depiction_;
1022 NSString *homepage_;
1023 Address *sponsor_;
1024 Address *author_;
1025 NSArray *tags_;
1026 NSString *role_;
1027
1028 NSArray *relationships_;
1029 }
1030
1031 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1032 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1033
1034 - (pkgCache::PkgIterator) iterator;
1035
1036 - (NSString *) section;
1037 - (Address *) maintainer;
1038 - (size_t) size;
1039 - (NSString *) description;
1040 - (NSString *) index;
1041
1042 - (NSDate *) seen;
1043
1044 - (NSString *) latest;
1045 - (NSString *) installed;
1046
1047 - (BOOL) valid;
1048 - (BOOL) upgradableAndEssential:(BOOL)essential;
1049 - (BOOL) essential;
1050 - (BOOL) broken;
1051 - (BOOL) unfiltered;
1052 - (BOOL) visible;
1053
1054 - (BOOL) half;
1055 - (BOOL) halfConfigured;
1056 - (BOOL) halfInstalled;
1057 - (BOOL) hasMode;
1058 - (NSString *) mode;
1059
1060 - (NSString *) id;
1061 - (NSString *) name;
1062 - (NSString *) tagline;
1063 - (NSString *) icon;
1064 - (NSString *) homepage;
1065 - (NSString *) depiction;
1066 - (Address *) author;
1067
1068 - (NSArray *) relationships;
1069
1070 - (Source *) source;
1071 - (NSString *) role;
1072
1073 - (BOOL) matches:(NSString *)text;
1074
1075 - (bool) hasSupportingRole;
1076 - (BOOL) hasTag:(NSString *)tag;
1077
1078 - (NSComparisonResult) compareByName:(Package *)package;
1079 - (NSComparisonResult) compareBySection:(Package *)package;
1080 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1081 - (NSComparisonResult) compareForChanges:(Package *)package;
1082
1083 - (void) install;
1084 - (void) remove;
1085
1086 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
1087 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1088 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1089 - (NSNumber *) isVisibleInSource:(Source *)source;
1090
1091 @end
1092
1093 @implementation Package
1094
1095 - (void) dealloc {
1096 if (source_ != nil)
1097 [source_ release];
1098
1099 [latest_ release];
1100 if (installed_ != nil)
1101 [installed_ release];
1102
1103 [id_ release];
1104 if (name_ != nil)
1105 [name_ release];
1106 [tagline_ release];
1107 if (icon_ != nil)
1108 [icon_ release];
1109 if (depiction_ != nil)
1110 [depiction_ release];
1111 if (homepage_ != nil)
1112 [homepage_ release];
1113 if (sponsor_ != nil)
1114 [sponsor_ release];
1115 if (author_ != nil)
1116 [author_ release];
1117 if (tags_ != nil)
1118 [tags_ release];
1119 if (role_ != nil)
1120 [role_ release];
1121
1122 if (relationships_ != nil)
1123 [relationships_ release];
1124
1125 [super dealloc];
1126 }
1127
1128 + (NSArray *) _attributeKeys {
1129 return [NSArray arrayWithObjects:@"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"section", @"size", @"source", @"sponsor", @"tagline", nil];
1130 }
1131
1132 - (NSArray *) attributeKeys {
1133 return [[self class] _attributeKeys];
1134 }
1135
1136 + (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1137 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1138 }
1139
1140 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1141 if ((self = [super init]) != nil) {
1142 iterator_ = iterator;
1143 database_ = database;
1144
1145 version_ = [database_ policy]->GetCandidateVer(iterator_);
1146 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1147
1148 if (!version_.end())
1149 file_ = version_.FileList();
1150 else {
1151 pkgCache &cache([database_ cache]);
1152 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1153 }
1154
1155 pkgCache::VerIterator current = iterator_.CurrentVer();
1156 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1157
1158 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1159
1160 if (!file_.end()) {
1161 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1162
1163 const char *begin, *end;
1164 parser->GetRec(begin, end);
1165
1166 name_ = Scour("Name", begin, end);
1167 if (name_ != nil)
1168 name_ = [name_ retain];
1169 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1170 icon_ = Scour("Icon", begin, end);
1171 if (icon_ != nil)
1172 icon_ = [icon_ retain];
1173 depiction_ = Scour("Depiction", begin, end);
1174 if (depiction_ != nil)
1175 depiction_ = [depiction_ retain];
1176 homepage_ = Scour("Homepage", begin, end);
1177 if (homepage_ == nil)
1178 homepage_ = Scour("Website", begin, end);
1179 if ([homepage_ isEqualToString:depiction_])
1180 homepage_ = nil;
1181 if (homepage_ != nil)
1182 homepage_ = [homepage_ retain];
1183 NSString *sponsor = Scour("Sponsor", begin, end);
1184 if (sponsor != nil)
1185 sponsor_ = [[Address addressWithString:sponsor] retain];
1186 NSString *author = Scour("Author", begin, end);
1187 if (author != nil)
1188 author_ = [[Address addressWithString:author] retain];
1189 NSString *tags = Scour("Tag", begin, end);
1190 if (tags != nil)
1191 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1192 }
1193
1194 if (tags_ != nil)
1195 for (int i(0), e([tags_ count]); i != e; ++i) {
1196 NSString *tag = [tags_ objectAtIndex:i];
1197 if ([tag hasPrefix:@"role::"]) {
1198 role_ = [[tag substringFromIndex:6] retain];
1199 break;
1200 }
1201 }
1202
1203 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1204 if (metadata == nil || [metadata count] == 0) {
1205 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1206 now_, @"FirstSeen",
1207 nil];
1208
1209 [Packages_ setObject:metadata forKey:id_];
1210 Changed_ = true;
1211 }
1212 } return self;
1213 }
1214
1215 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1216 return [[[Package alloc]
1217 initWithIterator:iterator
1218 database:database
1219 ] autorelease];
1220 }
1221
1222 - (pkgCache::PkgIterator) iterator {
1223 return iterator_;
1224 }
1225
1226 - (NSString *) section {
1227 const char *section = iterator_.Section();
1228 if (section == NULL)
1229 return nil;
1230
1231 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1232
1233 lookup:
1234 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1235 if (NSString *rename = [value objectForKey:@"Rename"]) {
1236 name = rename;
1237 goto lookup;
1238 }
1239
1240 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1241 }
1242
1243 - (Address *) maintainer {
1244 if (file_.end())
1245 return nil;
1246 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1247 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1248 }
1249
1250 - (size_t) size {
1251 return version_.end() ? 0 : version_->InstalledSize;
1252 }
1253
1254 - (NSString *) description {
1255 if (file_.end())
1256 return nil;
1257 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1258 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1259
1260 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1261 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1262 if ([lines count] < 2)
1263 return nil;
1264
1265 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1266 for (size_t i(1); i != [lines count]; ++i) {
1267 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1268 [trimmed addObject:trim];
1269 }
1270
1271 return [trimmed componentsJoinedByString:@"\n"];
1272 }
1273
1274 - (NSString *) index {
1275 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1276 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1277 }
1278
1279 - (NSDate *) seen {
1280 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1281 }
1282
1283 - (NSString *) latest {
1284 return latest_;
1285 }
1286
1287 - (NSString *) installed {
1288 return installed_;
1289 }
1290
1291 - (BOOL) valid {
1292 return !version_.end();
1293 }
1294
1295 - (BOOL) upgradableAndEssential:(BOOL)essential {
1296 pkgCache::VerIterator current = iterator_.CurrentVer();
1297
1298 if (current.end())
1299 return essential && [self essential];
1300 else {
1301 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1302 return !candidate.end() && candidate != current;
1303 }
1304 }
1305
1306 - (BOOL) essential {
1307 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1308 }
1309
1310 - (BOOL) broken {
1311 return [database_ cache][iterator_].InstBroken();
1312 }
1313
1314 - (BOOL) unfiltered {
1315 NSString *section = [self section];
1316 return section == nil || isSectionVisible(section);
1317 }
1318
1319 - (BOOL) visible {
1320 return [self hasSupportingRole] && [self unfiltered];
1321 }
1322
1323 - (BOOL) half {
1324 unsigned char current = iterator_->CurrentState;
1325 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1326 }
1327
1328 - (BOOL) halfConfigured {
1329 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1330 }
1331
1332 - (BOOL) halfInstalled {
1333 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1334 }
1335
1336 - (BOOL) hasMode {
1337 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1338 return state.Mode != pkgDepCache::ModeKeep;
1339 }
1340
1341 - (NSString *) mode {
1342 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1343
1344 switch (state.Mode) {
1345 case pkgDepCache::ModeDelete:
1346 if ((state.iFlags & pkgDepCache::Purge) != 0)
1347 return @"Purge";
1348 else
1349 return @"Remove";
1350 _assert(false);
1351 case pkgDepCache::ModeKeep:
1352 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1353 return nil;
1354 else
1355 return nil;
1356 _assert(false);
1357 case pkgDepCache::ModeInstall:
1358 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1359 return @"Reinstall";
1360 else switch (state.Status) {
1361 case -1:
1362 return @"Downgrade";
1363 case 0:
1364 return @"Install";
1365 case 1:
1366 return @"Upgrade";
1367 case 2:
1368 return @"New Install";
1369 default:
1370 _assert(false);
1371 }
1372 default:
1373 _assert(false);
1374 }
1375 }
1376
1377 - (NSString *) id {
1378 return id_;
1379 }
1380
1381 - (NSString *) name {
1382 return name_ == nil ? id_ : name_;
1383 }
1384
1385 - (NSString *) tagline {
1386 return tagline_;
1387 }
1388
1389 - (NSString *) icon {
1390 return icon_;
1391 }
1392
1393 - (NSString *) homepage {
1394 return homepage_;
1395 }
1396
1397 - (NSString *) depiction {
1398 return depiction_;
1399 }
1400
1401 - (Address *) sponsor {
1402 return sponsor_;
1403 }
1404
1405 - (Address *) author {
1406 return author_;
1407 }
1408
1409 - (NSArray *) relationships {
1410 return relationships_;
1411 }
1412
1413 - (Source *) source {
1414 if (!cached_) {
1415 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1416 cached_ = true;
1417 }
1418
1419 return source_;
1420 }
1421
1422 - (NSString *) role {
1423 return role_;
1424 }
1425
1426 - (BOOL) matches:(NSString *)text {
1427 if (text == nil)
1428 return NO;
1429
1430 NSRange range;
1431
1432 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1433 if (range.location != NSNotFound)
1434 return YES;
1435
1436 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1437 if (range.location != NSNotFound)
1438 return YES;
1439
1440 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1441 if (range.location != NSNotFound)
1442 return YES;
1443
1444 return NO;
1445 }
1446
1447 - (bool) hasSupportingRole {
1448 if (role_ == nil)
1449 return true;
1450 if ([role_ isEqualToString:@"enduser"])
1451 return true;
1452 if ([Role_ isEqualToString:@"User"])
1453 return false;
1454 if ([role_ isEqualToString:@"hacker"])
1455 return true;
1456 if ([Role_ isEqualToString:@"Hacker"])
1457 return false;
1458 if ([role_ isEqualToString:@"developer"])
1459 return true;
1460 if ([Role_ isEqualToString:@"Developer"])
1461 return false;
1462 _assert(false);
1463 }
1464
1465 - (BOOL) hasTag:(NSString *)tag {
1466 return tags_ == nil ? NO : [tags_ containsObject:tag];
1467 }
1468
1469 - (NSComparisonResult) compareByName:(Package *)package {
1470 NSString *lhs = [self name];
1471 NSString *rhs = [package name];
1472
1473 if ([lhs length] != 0 && [rhs length] != 0) {
1474 unichar lhc = [lhs characterAtIndex:0];
1475 unichar rhc = [rhs characterAtIndex:0];
1476
1477 if (isalpha(lhc) && !isalpha(rhc))
1478 return NSOrderedAscending;
1479 else if (!isalpha(lhc) && isalpha(rhc))
1480 return NSOrderedDescending;
1481 }
1482
1483 return [lhs compare:rhs options:CompareOptions_];
1484 }
1485
1486 - (NSComparisonResult) compareBySection:(Package *)package {
1487 NSString *lhs = [self section];
1488 NSString *rhs = [package section];
1489
1490 if (lhs == NULL && rhs != NULL)
1491 return NSOrderedAscending;
1492 else if (lhs != NULL && rhs == NULL)
1493 return NSOrderedDescending;
1494 else if (lhs != NULL && rhs != NULL) {
1495 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1496 if (result != NSOrderedSame)
1497 return result;
1498 }
1499
1500 return NSOrderedSame;
1501 }
1502
1503 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1504 NSString *lhs = [self section];
1505 NSString *rhs = [package section];
1506
1507 if (lhs == NULL && rhs != NULL)
1508 return NSOrderedAscending;
1509 else if (lhs != NULL && rhs == NULL)
1510 return NSOrderedDescending;
1511 else if (lhs != NULL && rhs != NULL) {
1512 NSComparisonResult result = [lhs compare:rhs];
1513 if (result != NSOrderedSame)
1514 return result;
1515 }
1516
1517 return [self compareByName:package];
1518 }
1519
1520 - (NSComparisonResult) compareForChanges:(Package *)package {
1521 BOOL lhs = [self upgradableAndEssential:YES];
1522 BOOL rhs = [package upgradableAndEssential:YES];
1523
1524 if (lhs != rhs)
1525 return lhs ? NSOrderedAscending : NSOrderedDescending;
1526 else if (!lhs) {
1527 switch ([[self seen] compare:[package seen]]) {
1528 case NSOrderedAscending:
1529 return NSOrderedDescending;
1530 case NSOrderedSame:
1531 break;
1532 case NSOrderedDescending:
1533 return NSOrderedAscending;
1534 default:
1535 _assert(false);
1536 }
1537 }
1538
1539 return [self compareByName:package];
1540 }
1541
1542 - (void) install {
1543 pkgProblemResolver *resolver = [database_ resolver];
1544 resolver->Clear(iterator_);
1545 resolver->Protect(iterator_);
1546 pkgCacheFile &cache([database_ cache]);
1547 cache->MarkInstall(iterator_, false);
1548 pkgDepCache::StateCache &state((*cache)[iterator_]);
1549 if (!state.Install())
1550 cache->SetReInstall(iterator_, true);
1551 }
1552
1553 - (void) remove {
1554 pkgProblemResolver *resolver = [database_ resolver];
1555 resolver->Clear(iterator_);
1556 resolver->Protect(iterator_);
1557 resolver->Remove(iterator_);
1558 [database_ cache]->MarkDelete(iterator_, true);
1559 }
1560
1561 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1562 return [NSNumber numberWithBool:(
1563 [self unfiltered] && [self matches:search]
1564 )];
1565 }
1566
1567 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1568 return [NSNumber numberWithBool:(
1569 (![number boolValue] || [self visible]) && [self installed] != nil
1570 )];
1571 }
1572
1573 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1574 NSString *section = [self section];
1575
1576 return [NSNumber numberWithBool:(
1577 [self visible] &&
1578 [self installed] == nil && (
1579 name == nil ||
1580 section == nil && [name length] == 0 ||
1581 [name isEqualToString:section]
1582 )
1583 )];
1584 }
1585
1586 - (NSNumber *) isVisibleInSource:(Source *)source {
1587 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1588 }
1589
1590 @end
1591 /* }}} */
1592 /* Section Class {{{ */
1593 @interface Section : NSObject {
1594 NSString *name_;
1595 size_t row_;
1596 size_t count_;
1597 }
1598
1599 - (NSComparisonResult) compareByName:(Section *)section;
1600 - (Section *) initWithName:(NSString *)name;
1601 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1602 - (NSString *) name;
1603 - (size_t) row;
1604 - (size_t) count;
1605 - (void) addToCount;
1606
1607 @end
1608
1609 @implementation Section
1610
1611 - (void) dealloc {
1612 [name_ release];
1613 [super dealloc];
1614 }
1615
1616 - (NSComparisonResult) compareByName:(Section *)section {
1617 NSString *lhs = [self name];
1618 NSString *rhs = [section name];
1619
1620 if ([lhs length] != 0 && [rhs length] != 0) {
1621 unichar lhc = [lhs characterAtIndex:0];
1622 unichar rhc = [rhs characterAtIndex:0];
1623
1624 if (isalpha(lhc) && !isalpha(rhc))
1625 return NSOrderedAscending;
1626 else if (!isalpha(lhc) && isalpha(rhc))
1627 return NSOrderedDescending;
1628 }
1629
1630 return [lhs compare:rhs options:CompareOptions_];
1631 }
1632
1633 - (Section *) initWithName:(NSString *)name {
1634 return [self initWithName:name row:0];
1635 }
1636
1637 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1638 if ((self = [super init]) != nil) {
1639 name_ = [name retain];
1640 row_ = row;
1641 } return self;
1642 }
1643
1644 - (NSString *) name {
1645 return name_;
1646 }
1647
1648 - (size_t) row {
1649 return row_;
1650 }
1651
1652 - (size_t) count {
1653 return count_;
1654 }
1655
1656 - (void) addToCount {
1657 ++count_;
1658 }
1659
1660 @end
1661 /* }}} */
1662
1663 int Finish_;
1664 NSArray *Finishes_;
1665
1666 /* Database Implementation {{{ */
1667 @implementation Database
1668
1669 - (void) dealloc {
1670 _assert(false);
1671 [super dealloc];
1672 }
1673
1674 - (void) _readCydia:(NSNumber *)fd {
1675 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1676
1677 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1678 std::istream is(&ib);
1679 std::string line;
1680
1681 static Pcre finish_r("^finish:([^:]*)$");
1682
1683 while (std::getline(is, line)) {
1684 const char *data(line.c_str());
1685 size_t size = line.size();
1686 fprintf(stderr, "C:%s\n", data);
1687
1688 if (finish_r(data, size)) {
1689 NSString *finish = finish_r[1];
1690 int index = [Finishes_ indexOfObject:finish];
1691 if (index != INT_MAX && index > Finish_)
1692 Finish_ = index;
1693 }
1694 }
1695
1696 [pool release];
1697 _assert(false);
1698 }
1699
1700 - (void) _readStatus:(NSNumber *)fd {
1701 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1702
1703 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1704 std::istream is(&ib);
1705 std::string line;
1706
1707 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1708 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1709
1710 while (std::getline(is, line)) {
1711 const char *data(line.c_str());
1712 size_t size = line.size();
1713 fprintf(stderr, "S:%s\n", data);
1714
1715 if (conffile_r(data, size)) {
1716 [delegate_ setConfigurationData:conffile_r[1]];
1717 } else if (strncmp(data, "status: ", 8) == 0) {
1718 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1719 [delegate_ setProgressTitle:string];
1720 } else if (pmstatus_r(data, size)) {
1721 std::string type([pmstatus_r[1] UTF8String]);
1722 NSString *id = pmstatus_r[2];
1723
1724 float percent([pmstatus_r[3] floatValue]);
1725 [delegate_ setProgressPercent:(percent / 100)];
1726
1727 NSString *string = pmstatus_r[4];
1728
1729 if (type == "pmerror")
1730 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1731 withObject:[NSArray arrayWithObjects:string, id, nil]
1732 waitUntilDone:YES
1733 ];
1734 else if (type == "pmstatus")
1735 [delegate_ setProgressTitle:string];
1736 else if (type == "pmconffile")
1737 [delegate_ setConfigurationData:string];
1738 else _assert(false);
1739 } else _assert(false);
1740 }
1741
1742 [pool release];
1743 _assert(false);
1744 }
1745
1746 - (void) _readOutput:(NSNumber *)fd {
1747 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1748
1749 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1750 std::istream is(&ib);
1751 std::string line;
1752
1753 while (std::getline(is, line)) {
1754 fprintf(stderr, "O:%s\n", line.c_str());
1755 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1756 }
1757
1758 [pool release];
1759 _assert(false);
1760 }
1761
1762 - (FILE *) input {
1763 return input_;
1764 }
1765
1766 - (Package *) packageWithName:(NSString *)name {
1767 if (static_cast<pkgDepCache *>(cache_) == NULL)
1768 return nil;
1769 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1770 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1771 }
1772
1773 - (Database *) init {
1774 if ((self = [super init]) != nil) {
1775 policy_ = NULL;
1776 records_ = NULL;
1777 resolver_ = NULL;
1778 fetcher_ = NULL;
1779 lock_ = NULL;
1780
1781 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1782 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1783
1784 int fds[2];
1785
1786 _assert(pipe(fds) != -1);
1787 cydiafd_ = fds[1];
1788
1789 _config->Set("APT::Keep-Fds::", cydiafd_);
1790 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1791
1792 [NSThread
1793 detachNewThreadSelector:@selector(_readCydia:)
1794 toTarget:self
1795 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1796 ];
1797
1798 _assert(pipe(fds) != -1);
1799 statusfd_ = fds[1];
1800
1801 [NSThread
1802 detachNewThreadSelector:@selector(_readStatus:)
1803 toTarget:self
1804 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1805 ];
1806
1807 _assert(pipe(fds) != -1);
1808 _assert(dup2(fds[0], 0) != -1);
1809 _assert(close(fds[0]) != -1);
1810
1811 input_ = fdopen(fds[1], "a");
1812
1813 _assert(pipe(fds) != -1);
1814 _assert(dup2(fds[1], 1) != -1);
1815 _assert(close(fds[1]) != -1);
1816
1817 [NSThread
1818 detachNewThreadSelector:@selector(_readOutput:)
1819 toTarget:self
1820 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1821 ];
1822 } return self;
1823 }
1824
1825 - (pkgCacheFile &) cache {
1826 return cache_;
1827 }
1828
1829 - (pkgDepCache::Policy *) policy {
1830 return policy_;
1831 }
1832
1833 - (pkgRecords *) records {
1834 return records_;
1835 }
1836
1837 - (pkgProblemResolver *) resolver {
1838 return resolver_;
1839 }
1840
1841 - (pkgAcquire &) fetcher {
1842 return *fetcher_;
1843 }
1844
1845 - (NSArray *) packages {
1846 return packages_;
1847 }
1848
1849 - (NSArray *) sources {
1850 return [sources_ allValues];
1851 }
1852
1853 - (void) reloadData {
1854 _error->Discard();
1855
1856 delete list_;
1857 list_ = NULL;
1858 manager_ = NULL;
1859 delete lock_;
1860 lock_ = NULL;
1861 delete fetcher_;
1862 fetcher_ = NULL;
1863 delete resolver_;
1864 resolver_ = NULL;
1865 delete records_;
1866 records_ = NULL;
1867 delete policy_;
1868 policy_ = NULL;
1869
1870 cache_.Close();
1871
1872 if (!cache_.Open(progress_, true)) {
1873 std::string error;
1874 if (!_error->PopMessage(error))
1875 _assert(false);
1876 _error->Discard();
1877 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1878
1879 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1880 [delegate_ repairWithSelector:@selector(configure)];
1881 else if (error == "The package lists or status file could not be parsed or opened.")
1882 [delegate_ repairWithSelector:@selector(update)];
1883 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1884 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1885 // else if (error == "The list of sources could not be read.")
1886 else _assert(false);
1887
1888 return;
1889 }
1890
1891 now_ = [[NSDate date] retain];
1892
1893 policy_ = new pkgDepCache::Policy();
1894 records_ = new pkgRecords(cache_);
1895 resolver_ = new pkgProblemResolver(cache_);
1896 fetcher_ = new pkgAcquire(&status_);
1897 lock_ = NULL;
1898
1899 list_ = new pkgSourceList();
1900 _assert(list_->ReadMainList());
1901
1902 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1903 _assert(pkgApplyStatus(cache_));
1904
1905 if (cache_->BrokenCount() != 0) {
1906 _assert(pkgFixBroken(cache_));
1907 _assert(cache_->BrokenCount() == 0);
1908 _assert(pkgMinimizeUpgrade(cache_));
1909 }
1910
1911 [sources_ removeAllObjects];
1912 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1913 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1914 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1915 [sources_
1916 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1917 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1918 ];
1919 }
1920
1921 [packages_ removeAllObjects];
1922 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1923 if (Package *package = [Package packageWithIterator:iterator database:self])
1924 [packages_ addObject:package];
1925
1926 [packages_ sortUsingSelector:@selector(compareByName:)];
1927 }
1928
1929 - (void) configure {
1930 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1931 system([dpkg UTF8String]);
1932 }
1933
1934 - (void) clean {
1935 if (lock_ != NULL)
1936 return;
1937
1938 FileFd Lock;
1939 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1940 _assert(!_error->PendingError());
1941
1942 pkgAcquire fetcher;
1943 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1944
1945 class LogCleaner :
1946 public pkgArchiveCleaner
1947 {
1948 protected:
1949 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1950 unlink(File);
1951 }
1952 } cleaner;
1953
1954 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1955 std::string error;
1956 while (_error->PopMessage(error))
1957 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1958 }
1959 }
1960
1961 - (void) prepare {
1962 pkgRecords records(cache_);
1963
1964 lock_ = new FileFd();
1965 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1966 _assert(!_error->PendingError());
1967
1968 pkgSourceList list;
1969 // XXX: explain this with an error message
1970 _assert(list.ReadMainList());
1971
1972 manager_ = (_system->CreatePM(cache_));
1973 _assert(manager_->GetArchives(fetcher_, &list, &records));
1974 _assert(!_error->PendingError());
1975 }
1976
1977 - (void) perform {
1978 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1979 pkgSourceList list;
1980 _assert(list.ReadMainList());
1981 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1982 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1983 }
1984
1985 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
1986 _trace();
1987 return;
1988 }
1989
1990 bool failed = false;
1991 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
1992 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
1993 continue;
1994
1995 std::string uri = (*item)->DescURI();
1996 std::string error = (*item)->ErrorText;
1997
1998 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
1999 failed = true;
2000
2001 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2002 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2003 waitUntilDone:YES
2004 ];
2005 }
2006
2007 if (failed) {
2008 _trace();
2009 return;
2010 }
2011
2012 _system->UnLock();
2013 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2014
2015 if (_error->PendingError()) {
2016 _trace();
2017 return;
2018 }
2019
2020 if (result == pkgPackageManager::Failed) {
2021 _trace();
2022 return;
2023 }
2024
2025 if (result != pkgPackageManager::Completed) {
2026 _trace();
2027 return;
2028 }
2029
2030 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2031 pkgSourceList list;
2032 _assert(list.ReadMainList());
2033 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2034 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2035 }
2036
2037 if (![before isEqualToArray:after])
2038 [self update];
2039 }
2040
2041 - (void) upgrade {
2042 _assert(pkgDistUpgrade(cache_));
2043 }
2044
2045 - (void) update {
2046 [self updateWithStatus:status_];
2047 }
2048
2049 - (void) updateWithStatus:(Status &)status {
2050 pkgSourceList list;
2051 _assert(list.ReadMainList());
2052
2053 FileFd lock;
2054 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2055 _assert(!_error->PendingError());
2056
2057 pkgAcquire fetcher(&status);
2058 _assert(list.GetIndexes(&fetcher));
2059
2060 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2061 bool failed = false;
2062 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2063 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2064 (*item)->Finished();
2065 failed = true;
2066 }
2067
2068 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2069 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2070 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2071 }
2072
2073 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2074 Changed_ = true;
2075 }
2076 }
2077
2078 - (void) setDelegate:(id)delegate {
2079 delegate_ = delegate;
2080 status_.setDelegate(delegate);
2081 progress_.setDelegate(delegate);
2082 }
2083
2084 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2085 pkgIndexFile *index(NULL);
2086 list_->FindIndex(file, index);
2087 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2088 }
2089
2090 @end
2091 /* }}} */
2092
2093 /* Confirmation View {{{ */
2094 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2095 if ([packages count] == 0)
2096 return;
2097
2098 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2099 [fields setObject:text forKey:key];
2100
2101 CGColor blue(space_, 0, 0, 0.4, 1);
2102 [text setTextColor:[UIColor colorWithCGColor:blue]];
2103 }
2104
2105 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2106 if (!iterator.end())
2107 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2108 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2109 continue;
2110 pkgCache::PkgIterator package(dep.TargetPkg());
2111 if (package.end())
2112 continue;
2113 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2114 return true;
2115 }
2116
2117 return false;
2118 }
2119
2120 @protocol ConfirmationViewDelegate
2121 - (void) cancel;
2122 - (void) confirm;
2123 @end
2124
2125 @interface ConfirmationView : UIView {
2126 Database *database_;
2127 id delegate_;
2128 UITransitionView *transition_;
2129 UIView *overlay_;
2130 UINavigationBar *navbar_;
2131 UIPreferencesTable *table_;
2132 NSMutableDictionary *fields_;
2133 UIActionSheet *essential_;
2134 BOOL substrate_;
2135 }
2136
2137 - (void) cancel;
2138
2139 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2140
2141 @end
2142
2143 @implementation ConfirmationView
2144
2145 - (void) dealloc {
2146 [navbar_ setDelegate:nil];
2147 [transition_ setDelegate:nil];
2148 [table_ setDataSource:nil];
2149
2150 [transition_ release];
2151 [overlay_ release];
2152 [navbar_ release];
2153 [table_ release];
2154 [fields_ release];
2155 if (essential_ != nil)
2156 [essential_ release];
2157 [super dealloc];
2158 }
2159
2160 - (void) cancel {
2161 [transition_ transition:7 toView:nil];
2162 [delegate_ cancel];
2163 }
2164
2165 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2166 if (from != nil && to == nil)
2167 [self removeFromSuperview];
2168 }
2169
2170 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2171 switch (button) {
2172 case 0:
2173 if (essential_ != nil)
2174 [essential_ popupAlertAnimated:YES];
2175 else {
2176 if (substrate_)
2177 Finish_ = 2;
2178 [delegate_ confirm];
2179 }
2180 break;
2181
2182 case 1:
2183 [self cancel];
2184 break;
2185 }
2186 }
2187
2188 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2189 NSString *context = [sheet context];
2190
2191 if ([context isEqualToString:@"remove"])
2192 switch (button) {
2193 case 1:
2194 [self cancel];
2195 break;
2196 case 2:
2197 if (substrate_)
2198 Finish_ = 2;
2199 [delegate_ confirm];
2200 break;
2201 default:
2202 _assert(false);
2203 }
2204 else if ([context isEqualToString:@"unable"])
2205 [self cancel];
2206
2207 [sheet dismiss];
2208 }
2209
2210 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2211 return 2;
2212 }
2213
2214 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2215 switch (group) {
2216 case 0: return @"Statistics";
2217 case 1: return @"Modifications";
2218
2219 default: _assert(false);
2220 }
2221 }
2222
2223 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2224 switch (group) {
2225 case 0: return 3;
2226 case 1: return [fields_ count];
2227
2228 default: _assert(false);
2229 }
2230 }
2231
2232 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2233 if (group != 1 || row == -1)
2234 return proposed;
2235 else {
2236 _assert(size_t(row) < [fields_ count]);
2237 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2238 }
2239 }
2240
2241 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2242 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2243 [cell setShowSelection:NO];
2244
2245 switch (group) {
2246 case 0: switch (row) {
2247 case 0: {
2248 [cell setTitle:@"Downloading"];
2249 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2250 } break;
2251
2252 case 1: {
2253 [cell setTitle:@"Resuming At"];
2254 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2255 } break;
2256
2257 case 2: {
2258 double size([database_ cache]->UsrSize());
2259
2260 if (size < 0) {
2261 [cell setTitle:@"Disk Freeing"];
2262 [cell setValue:SizeString(-size)];
2263 } else {
2264 [cell setTitle:@"Disk Using"];
2265 [cell setValue:SizeString(size)];
2266 }
2267 } break;
2268
2269 default: _assert(false);
2270 } break;
2271
2272 case 1:
2273 _assert(size_t(row) < [fields_ count]);
2274 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2275 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2276 break;
2277
2278 default: _assert(false);
2279 }
2280
2281 return cell;
2282 }
2283
2284 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2285 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2286 database_ = database;
2287 delegate_ = delegate;
2288
2289 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2290 [self addSubview:transition_];
2291
2292 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2293
2294 CGSize navsize = [UINavigationBar defaultSize];
2295 CGRect navrect = {{0, 0}, navsize};
2296 CGRect bounds = [overlay_ bounds];
2297
2298 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2299 [navbar_ setDelegate:self];
2300
2301 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2302 [navbar_ pushNavigationItem:navitem];
2303 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2304
2305 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2306
2307 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2308 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2309 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2310 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2311 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2312
2313 bool remove(false);
2314
2315 pkgDepCache::Policy *policy([database_ policy]);
2316
2317 pkgCacheFile &cache([database_ cache]);
2318 NSArray *packages = [database_ packages];
2319 for (size_t i(0), e = [packages count]; i != e; ++i) {
2320 Package *package = [packages objectAtIndex:i];
2321 pkgCache::PkgIterator iterator = [package iterator];
2322 pkgDepCache::StateCache &state(cache[iterator]);
2323
2324 NSString *name([package name]);
2325
2326 if (state.NewInstall())
2327 [installing addObject:name];
2328 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2329 [reinstalling addObject:name];
2330 else if (state.Upgrade())
2331 [upgrading addObject:name];
2332 else if (state.Downgrade())
2333 [downgrading addObject:name];
2334 else if (state.Delete()) {
2335 if ([package essential])
2336 remove = true;
2337 [removing addObject:name];
2338 } else continue;
2339
2340 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2341 substrate_ |= DepSubstrate(iterator.CurrentVer());
2342 }
2343
2344 if (!remove)
2345 essential_ = nil;
2346 else if (Advanced_ || true) {
2347 essential_ = [[UIActionSheet alloc]
2348 initWithTitle:@"Removing Essentials"
2349 buttons:[NSArray arrayWithObjects:
2350 @"Cancel Operation (Safe)",
2351 @"Force Removal (Unsafe)",
2352 nil]
2353 defaultButtonIndex:0
2354 delegate:self
2355 context:@"remove"
2356 ];
2357
2358 #ifndef __OBJC2__
2359 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2360 #endif
2361 [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."];
2362 } else {
2363 essential_ = [[UIActionSheet alloc]
2364 initWithTitle:@"Unable to Comply"
2365 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2366 defaultButtonIndex:0
2367 delegate:self
2368 context:@"unable"
2369 ];
2370
2371 [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."];
2372 }
2373
2374 AddTextView(fields_, installing, @"Installing");
2375 AddTextView(fields_, reinstalling, @"Reinstalling");
2376 AddTextView(fields_, upgrading, @"Upgrading");
2377 AddTextView(fields_, downgrading, @"Downgrading");
2378 AddTextView(fields_, removing, @"Removing");
2379
2380 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2381 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2382 )];
2383
2384 [table_ setDataSource:self];
2385 [table_ reloadData];
2386
2387 [overlay_ addSubview:navbar_];
2388 [overlay_ addSubview:table_];
2389
2390 [view addSubview:self];
2391
2392 [transition_ setDelegate:self];
2393
2394 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2395 [transition_ transition:0 toView:blank];
2396 [transition_ transition:3 toView:overlay_];
2397 } return self;
2398 }
2399
2400 @end
2401 /* }}} */
2402
2403 /* Progress Data {{{ */
2404 @interface ProgressData : NSObject {
2405 SEL selector_;
2406 id target_;
2407 id object_;
2408 }
2409
2410 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2411
2412 - (SEL) selector;
2413 - (id) target;
2414 - (id) object;
2415 @end
2416
2417 @implementation ProgressData
2418
2419 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2420 if ((self = [super init]) != nil) {
2421 selector_ = selector;
2422 target_ = target;
2423 object_ = object;
2424 } return self;
2425 }
2426
2427 - (SEL) selector {
2428 return selector_;
2429 }
2430
2431 - (id) target {
2432 return target_;
2433 }
2434
2435 - (id) object {
2436 return object_;
2437 }
2438
2439 @end
2440 /* }}} */
2441 /* Progress View {{{ */
2442 @interface ProgressView : UIView <
2443 ConfigurationDelegate,
2444 ProgressDelegate
2445 > {
2446 _transient Database *database_;
2447 UIView *view_;
2448 UIView *background_;
2449 UITransitionView *transition_;
2450 UIView *overlay_;
2451 UINavigationBar *navbar_;
2452 UIProgressBar *progress_;
2453 UITextView *output_;
2454 UITextLabel *status_;
2455 UIPushButton *close_;
2456 id delegate_;
2457 BOOL running_;
2458 SHA1SumValue springlist_;
2459 size_t received_;
2460 NSTimeInterval last_;
2461 }
2462
2463 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2464
2465 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2466 - (void) setContentView:(UIView *)view;
2467 - (void) resetView;
2468
2469 - (void) _retachThread;
2470 - (void) _detachNewThreadData:(ProgressData *)data;
2471 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2472
2473 - (BOOL) isRunning;
2474
2475 @end
2476
2477 @protocol ProgressViewDelegate
2478 - (void) progressViewIsComplete:(ProgressView *)sender;
2479 @end
2480
2481 @implementation ProgressView
2482
2483 - (void) dealloc {
2484 [transition_ setDelegate:nil];
2485 [navbar_ setDelegate:nil];
2486
2487 [view_ release];
2488 if (background_ != nil)
2489 [background_ release];
2490 [transition_ release];
2491 [overlay_ release];
2492 [navbar_ release];
2493 [progress_ release];
2494 [output_ release];
2495 [status_ release];
2496 [close_ release];
2497 [super dealloc];
2498 }
2499
2500 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2501 if (bootstrap_ && from == overlay_ && to == view_)
2502 exit(0);
2503 }
2504
2505 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2506 if ((self = [super initWithFrame:frame]) != nil) {
2507 database_ = database;
2508 delegate_ = delegate;
2509
2510 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2511 [transition_ setDelegate:self];
2512
2513 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2514
2515 if (bootstrap_)
2516 [overlay_ setBackgroundColor:[UIColor blackColor]];
2517 else {
2518 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2519 [background_ setBackgroundColor:[UIColor blackColor]];
2520 [self addSubview:background_];
2521 }
2522
2523 [self addSubview:transition_];
2524
2525 CGSize navsize = [UINavigationBar defaultSize];
2526 CGRect navrect = {{0, 0}, navsize};
2527
2528 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2529 [overlay_ addSubview:navbar_];
2530
2531 [navbar_ setBarStyle:1];
2532 [navbar_ setDelegate:self];
2533
2534 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2535 [navbar_ pushNavigationItem:navitem];
2536
2537 CGRect bounds = [overlay_ bounds];
2538 CGSize prgsize = [UIProgressBar defaultSize];
2539
2540 CGRect prgrect = {{
2541 (bounds.size.width - prgsize.width) / 2,
2542 bounds.size.height - prgsize.height - 20
2543 }, prgsize};
2544
2545 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2546 [progress_ setStyle:0];
2547
2548 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2549 10,
2550 bounds.size.height - prgsize.height - 50,
2551 bounds.size.width - 20,
2552 24
2553 )];
2554
2555 [status_ setColor:[UIColor whiteColor]];
2556 [status_ setBackgroundColor:[UIColor clearColor]];
2557
2558 [status_ setCentersHorizontally:YES];
2559 //[status_ setFont:font];
2560
2561 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2562 10,
2563 navrect.size.height + 20,
2564 bounds.size.width - 20,
2565 bounds.size.height - navsize.height - 62 - navrect.size.height
2566 )];
2567
2568 //[output_ setTextFont:@"Courier New"];
2569 [output_ setTextSize:12];
2570
2571 [output_ setTextColor:[UIColor whiteColor]];
2572 [output_ setBackgroundColor:[UIColor clearColor]];
2573
2574 [output_ setMarginTop:0];
2575 [output_ setAllowsRubberBanding:YES];
2576 [output_ setEditable:NO];
2577
2578 [overlay_ addSubview:output_];
2579
2580 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2581 10,
2582 bounds.size.height - prgsize.height - 50,
2583 bounds.size.width - 20,
2584 32 + prgsize.height
2585 )];
2586
2587 [close_ setAutosizesToFit:NO];
2588 [close_ setDrawsShadow:YES];
2589 [close_ setStretchBackground:YES];
2590 [close_ setEnabled:YES];
2591
2592 UIFont *bold = [UIFont boldSystemFontOfSize:22];
2593 [close_ setTitleFont:bold];
2594
2595 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2596 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2597 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2598 } return self;
2599 }
2600
2601 - (void) setContentView:(UIView *)view {
2602 view_ = [view retain];
2603 }
2604
2605 - (void) resetView {
2606 [transition_ transition:6 toView:view_];
2607 }
2608
2609 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2610 NSString *context = [sheet context];
2611 if ([context isEqualToString:@"conffile"]) {
2612 FILE *input = [database_ input];
2613
2614 switch (button) {
2615 case 1:
2616 fprintf(input, "N\n");
2617 fflush(input);
2618 break;
2619 case 2:
2620 fprintf(input, "Y\n");
2621 fflush(input);
2622 break;
2623 default:
2624 _assert(false);
2625 }
2626 }
2627
2628 [sheet dismiss];
2629 }
2630
2631 - (void) closeButtonPushed {
2632 switch (Finish_) {
2633 case 0:
2634 [delegate_ progressViewIsComplete:self];
2635 [self resetView];
2636 break;
2637
2638 case 1:
2639 [delegate_ suspendWithAnimation:YES];
2640 break;
2641
2642 case 2:
2643 system("launchctl stop com.apple.SpringBoard");
2644 break;
2645
2646 case 3:
2647 system("launchctl unload /System/Library/LaunchDaemons/com.apple.SpringBoard.plist; launchctl load /System/Library/LaunchDaemons/com.apple.SpringBoard.plist");
2648 break;
2649
2650 case 4:
2651 system("reboot");
2652 break;
2653 }
2654 }
2655
2656 - (void) _retachThread {
2657 UINavigationItem *item = [navbar_ topItem];
2658 [item setTitle:@"Complete"];
2659
2660 [overlay_ addSubview:close_];
2661 [progress_ removeFromSuperview];
2662 [status_ removeFromSuperview];
2663
2664 {
2665 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2666 MMap mmap(file, MMap::ReadOnly);
2667 SHA1Summation sha1;
2668 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2669 if (!(springlist_ == sha1.Result()))
2670 Finish_ = 3;
2671 }
2672
2673 switch (Finish_) {
2674 case 0: [close_ setTitle:@"Return to Cydia"]; break;
2675 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
2676 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
2677 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
2678 case 4: [close_ setTitle:@"Reboot Device"]; break;
2679 }
2680
2681 #ifdef __OBJC2__
2682 notify_post("com.apple.mobile.application_installed");
2683 #endif
2684
2685 [delegate_ setStatusBarShowsProgress:NO];
2686
2687 running_ = NO;
2688 }
2689
2690 - (void) _detachNewThreadData:(ProgressData *)data {
2691 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2692
2693 [[data target] performSelector:[data selector] withObject:[data object]];
2694 [data release];
2695
2696 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2697
2698 [pool release];
2699 }
2700
2701 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2702 UINavigationItem *item = [navbar_ topItem];
2703 [item setTitle:title];
2704
2705 [status_ setText:nil];
2706 [output_ setText:@""];
2707 [progress_ setProgress:0];
2708
2709 received_ = 0;
2710 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
2711
2712 [close_ removeFromSuperview];
2713 [overlay_ addSubview:progress_];
2714 [overlay_ addSubview:status_];
2715
2716 [delegate_ setStatusBarShowsProgress:YES];
2717 running_ = YES;
2718
2719 {
2720 FileFd file("/System/Library/LaunchDaemons/com.apple.SpringBoard.plist", FileFd::ReadOnly);
2721 MMap mmap(file, MMap::ReadOnly);
2722 SHA1Summation sha1;
2723 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
2724 springlist_ = sha1.Result();
2725 }
2726
2727 [transition_ transition:6 toView:overlay_];
2728
2729 [NSThread
2730 detachNewThreadSelector:@selector(_detachNewThreadData:)
2731 toTarget:self
2732 withObject:[[ProgressData alloc]
2733 initWithSelector:selector
2734 target:target
2735 object:object
2736 ]
2737 ];
2738 }
2739
2740 - (void) repairWithSelector:(SEL)selector {
2741 [self
2742 detachNewThreadSelector:selector
2743 toTarget:database_
2744 withObject:nil
2745 title:@"Repairing"
2746 ];
2747 }
2748
2749 - (void) setConfigurationData:(NSString *)data {
2750 [self
2751 performSelectorOnMainThread:@selector(_setConfigurationData:)
2752 withObject:data
2753 waitUntilDone:YES
2754 ];
2755 }
2756
2757 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2758 Package *package = id == nil ? nil : [database_ packageWithName:id];
2759
2760 UIActionSheet *sheet = [[[UIActionSheet alloc]
2761 initWithTitle:(package == nil ? @"Source Error" : [package name])
2762 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2763 defaultButtonIndex:0
2764 delegate:self
2765 context:@"error"
2766 ] autorelease];
2767
2768 [sheet setBodyText:error];
2769 [sheet popupAlertAnimated:YES];
2770 }
2771
2772 - (void) setProgressTitle:(NSString *)title {
2773 [self
2774 performSelectorOnMainThread:@selector(_setProgressTitle:)
2775 withObject:title
2776 waitUntilDone:YES
2777 ];
2778 }
2779
2780 - (void) setProgressPercent:(float)percent {
2781 [self
2782 performSelectorOnMainThread:@selector(_setProgressPercent:)
2783 withObject:[NSNumber numberWithFloat:percent]
2784 waitUntilDone:YES
2785 ];
2786 }
2787
2788 - (void) startProgress {
2789 last_ = [NSDate timeIntervalSinceReferenceDate];
2790 }
2791
2792 - (void) addProgressOutput:(NSString *)output {
2793 [self
2794 performSelectorOnMainThread:@selector(_addProgressOutput:)
2795 withObject:output
2796 waitUntilDone:YES
2797 ];
2798 }
2799
2800 - (bool) isCancelling:(size_t)received {
2801 if (last_ != 0) {
2802 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
2803 if (received_ != received) {
2804 received_ = received;
2805 last_ = now;
2806 } else if (now - last_ > 30)
2807 return true;
2808 }
2809
2810 return false;
2811 }
2812
2813 - (void) _setConfigurationData:(NSString *)data {
2814 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2815
2816 _assert(conffile_r(data));
2817
2818 NSString *ofile = conffile_r[1];
2819 //NSString *nfile = conffile_r[2];
2820
2821 UIActionSheet *sheet = [[[UIActionSheet alloc]
2822 initWithTitle:@"Configuration Upgrade"
2823 buttons:[NSArray arrayWithObjects:
2824 @"Keep My Old Copy",
2825 @"Accept The New Copy",
2826 // XXX: @"See What Changed",
2827 nil]
2828 defaultButtonIndex:0
2829 delegate:self
2830 context:@"conffile"
2831 ] autorelease];
2832
2833 [sheet setBodyText:[NSString stringWithFormat:
2834 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2835 , ofile]];
2836
2837 [sheet popupAlertAnimated:YES];
2838 }
2839
2840 - (void) _setProgressTitle:(NSString *)title {
2841 [status_ setText:title];
2842 }
2843
2844 - (void) _setProgressPercent:(NSNumber *)percent {
2845 [progress_ setProgress:[percent floatValue]];
2846 }
2847
2848 - (void) _addProgressOutput:(NSString *)output {
2849 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2850 CGSize size = [output_ contentSize];
2851 CGRect rect = {{0, size.height}, {size.width, 0}};
2852 [output_ scrollRectToVisible:rect animated:YES];
2853 }
2854
2855 - (BOOL) isRunning {
2856 return running_;
2857 }
2858
2859 @end
2860 /* }}} */
2861
2862 /* Package Cell {{{ */
2863 @interface PackageCell : UISimpleTableCell {
2864 UIImage *icon_;
2865 NSString *name_;
2866 NSString *description_;
2867 NSString *source_;
2868 //UIImageView *trusted_;
2869 #ifdef USE_BADGES
2870 UIImageView *badge_;
2871 UITextLabel *status_;
2872 #endif
2873 }
2874
2875 - (PackageCell *) init;
2876 - (void) setPackage:(Package *)package;
2877
2878 + (int) heightForPackage:(Package *)package;
2879
2880 @end
2881
2882 @implementation PackageCell
2883
2884 - (void) clearPackage {
2885 if (icon_ != nil) {
2886 [icon_ release];
2887 icon_ = nil;
2888 }
2889
2890 if (name_ != nil) {
2891 [name_ release];
2892 name_ = nil;
2893 }
2894
2895 if (description_ != nil) {
2896 [description_ release];
2897 description_ = nil;
2898 }
2899
2900 if (source_ != nil) {
2901 [source_ release];
2902 source_ = nil;
2903 }
2904 }
2905
2906 - (void) dealloc {
2907 [self clearPackage];
2908 #ifdef USE_BADGES
2909 [badge_ release];
2910 [status_ release];
2911 #endif
2912 //[trusted_ release];
2913 [super dealloc];
2914 }
2915
2916 - (PackageCell *) init {
2917 if ((self = [super init]) != nil) {
2918 #ifdef USE_BADGES
2919 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2920
2921 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2922 [status_ setBackgroundColor:[UIColor clearColor]];
2923 [status_ setFont:small];
2924 #endif
2925 } return self;
2926 }
2927
2928 - (void) setPackage:(Package *)package {
2929 [self clearPackage];
2930
2931 Source *source = [package source];
2932
2933 icon_ = nil;
2934 if (NSString *icon = [package icon])
2935 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2936 if (icon_ == nil) if (NSString *section = [package section])
2937 icon_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, Simplify(section)]];
2938 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2939 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2940 if (icon_ == nil)
2941 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2942
2943 icon_ = [icon_ retain];
2944
2945 name_ = [[package name] retain];
2946 description_ = [[package tagline] retain];
2947
2948 NSString *label = nil;
2949 bool trusted = false;
2950
2951 if (source != nil) {
2952 label = [source label];
2953 trusted = [source trusted];
2954 } else if ([[package id] isEqualToString:@"firmware"])
2955 label = @"Apple";
2956 else
2957 label = @"Unknown/Local";
2958
2959 NSString *from = [NSString stringWithFormat:@"from %@", label];
2960
2961 NSString *section = Simplify([package section]);
2962 if (section != nil && ![section isEqualToString:label])
2963 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2964
2965 source_ = [from retain];
2966
2967 #ifdef USE_BADGES
2968 [badge_ removeFromSuperview];
2969 [status_ removeFromSuperview];
2970
2971 if (NSString *mode = [package mode]) {
2972 [badge_ setImage:[UIImage applicationImageNamed:
2973 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2974 ]];
2975
2976 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2977 [status_ setColor:Blueish_];
2978 } else if ([package half]) {
2979 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2980 [status_ setText:@"Package Damaged"];
2981 [status_ setColor:[UIColor redColor]];
2982 } else {
2983 [badge_ setImage:nil];
2984 [status_ setText:nil];
2985 goto done;
2986 }
2987
2988 [self addSubview:badge_];
2989 [self addSubview:status_];
2990 done:;
2991 #endif
2992 }
2993
2994 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2995 if (icon_ != nil) {
2996 CGRect rect;
2997 rect.size = [icon_ size];
2998
2999 rect.size.width /= 2;
3000 rect.size.height /= 2;
3001
3002 rect.origin.x = 25 - rect.size.width / 2;
3003 rect.origin.y = 25 - rect.size.height / 2;
3004
3005 [icon_ drawInRect:rect];
3006 }
3007
3008 if (selected)
3009 UISetColor(White_);
3010
3011 if (!selected)
3012 UISetColor(Black_);
3013 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3014 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3015
3016 if (!selected)
3017 UISetColor(Gray_);
3018 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3019
3020 [super drawContentInRect:rect selected:selected];
3021 }
3022
3023 + (int) heightForPackage:(Package *)package {
3024 NSString *tagline([package tagline]);
3025 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3026 #ifdef USE_BADGES
3027 if ([package hasMode] || [package half])
3028 return height + 96;
3029 else
3030 #endif
3031 return height + 73;
3032 }
3033
3034 @end
3035 /* }}} */
3036 /* Section Cell {{{ */
3037 @interface SectionCell : UISimpleTableCell {
3038 NSString *section_;
3039 NSString *name_;
3040 NSString *count_;
3041 UIImage *icon_;
3042 _UISwitchSlider *switch_;
3043 BOOL editing_;
3044 }
3045
3046 - (id) init;
3047 - (void) setSection:(Section *)section editing:(BOOL)editing;
3048
3049 @end
3050
3051 @implementation SectionCell
3052
3053 - (void) clearSection {
3054 if (section_ != nil) {
3055 [section_ release];
3056 section_ = nil;
3057 }
3058
3059 if (name_ != nil) {
3060 [name_ release];
3061 name_ = nil;
3062 }
3063
3064 if (count_ != nil) {
3065 [count_ release];
3066 count_ = nil;
3067 }
3068 }
3069
3070 - (void) dealloc {
3071 [self clearSection];
3072 [icon_ release];
3073 [switch_ release];
3074 [super dealloc];
3075 }
3076
3077 - (id) init {
3078 if ((self = [super init]) != nil) {
3079 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3080
3081 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3082 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3083 } return self;
3084 }
3085
3086 - (void) onSwitch:(id)sender {
3087 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3088 if (metadata == nil) {
3089 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3090 [Sections_ setObject:metadata forKey:section_];
3091 }
3092
3093 Changed_ = true;
3094 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3095 }
3096
3097 - (void) setSection:(Section *)section editing:(BOOL)editing {
3098 if (editing != editing_) {
3099 if (editing_)
3100 [switch_ removeFromSuperview];
3101 else
3102 [self addSubview:switch_];
3103 editing_ = editing;
3104 }
3105
3106 [self clearSection];
3107
3108 if (section == nil) {
3109 name_ = [@"All Packages" retain];
3110 count_ = nil;
3111 } else {
3112 section_ = [section name];
3113 if (section_ != nil)
3114 section_ = [section_ retain];
3115 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3116 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3117
3118 if (editing_)
3119 [switch_ setValue:isSectionVisible(section_) animated:NO];
3120 }
3121 }
3122
3123 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3124 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3125
3126 if (selected)
3127 UISetColor(White_);
3128
3129 if (!selected)
3130 UISetColor(Black_);
3131 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3132
3133 CGSize size = [count_ sizeWithFont:Font14_];
3134
3135 UISetColor(White_);
3136 if (count_ != nil)
3137 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3138
3139 [super drawContentInRect:rect selected:selected];
3140 }
3141
3142 @end
3143 /* }}} */
3144
3145 /* File Table {{{ */
3146 @interface FileTable : RVPage {
3147 _transient Database *database_;
3148 Package *package_;
3149 NSString *name_;
3150 NSMutableArray *files_;
3151 UITable *list_;
3152 }
3153
3154 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3155 - (void) setPackage:(Package *)package;
3156
3157 @end
3158
3159 @implementation FileTable
3160
3161 - (void) dealloc {
3162 if (package_ != nil)
3163 [package_ release];
3164 if (name_ != nil)
3165 [name_ release];
3166 [files_ release];
3167 [list_ release];
3168 [super dealloc];
3169 }
3170
3171 - (int) numberOfRowsInTable:(UITable *)table {
3172 return files_ == nil ? 0 : [files_ count];
3173 }
3174
3175 - (float) table:(UITable *)table heightForRow:(int)row {
3176 return 24;
3177 }
3178
3179 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3180 if (reusing == nil) {
3181 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3182 UIFont *font = [UIFont systemFontOfSize:16];
3183 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3184 }
3185 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3186 return reusing;
3187 }
3188
3189 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3190 return NO;
3191 }
3192
3193 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3194 if ((self = [super initWithBook:book]) != nil) {
3195 database_ = database;
3196
3197 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3198
3199 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3200 [self addSubview:list_];
3201
3202 UITableColumn *column = [[[UITableColumn alloc]
3203 initWithTitle:@"Name"
3204 identifier:@"name"
3205 width:[self frame].size.width
3206 ] autorelease];
3207
3208 [list_ setDataSource:self];
3209 [list_ setSeparatorStyle:1];
3210 [list_ addTableColumn:column];
3211 [list_ setDelegate:self];
3212 [list_ setReusesTableCells:YES];
3213 } return self;
3214 }
3215
3216 - (void) setPackage:(Package *)package {
3217 if (package_ != nil) {
3218 [package_ autorelease];
3219 package_ = nil;
3220 }
3221
3222 if (name_ != nil) {
3223 [name_ release];
3224 name_ = nil;
3225 }
3226
3227 [files_ removeAllObjects];
3228
3229 if (package != nil) {
3230 package_ = [package retain];
3231 name_ = [[package id] retain];
3232
3233 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3234
3235 {
3236 std::ifstream fin([path UTF8String]);
3237 std::string line;
3238 while (std::getline(fin, line))
3239 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3240 }
3241
3242 if ([files_ count] != 0) {
3243 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3244 [files_ removeObjectAtIndex:0];
3245 [files_ sortUsingSelector:@selector(compareByPath:)];
3246
3247 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3248 [stack addObject:@"/"];
3249
3250 for (int i(0), e([files_ count]); i != e; ++i) {
3251 NSString *file = [files_ objectAtIndex:i];
3252 while (![file hasPrefix:[stack lastObject]])
3253 [stack removeLastObject];
3254 NSString *directory = [stack lastObject];
3255 [stack addObject:[file stringByAppendingString:@"/"]];
3256 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3257 ([stack count] - 2) * 3, "",
3258 [file substringFromIndex:[directory length]]
3259 ]];
3260 }
3261 }
3262 }
3263
3264 [list_ reloadData];
3265 }
3266
3267 - (void) resetViewAnimated:(BOOL)animated {
3268 [list_ resetViewAnimated:animated];
3269 }
3270
3271 - (void) reloadData {
3272 [self setPackage:[database_ packageWithName:name_]];
3273 [self reloadButtons];
3274 }
3275
3276 - (NSString *) title {
3277 return @"Installed Files";
3278 }
3279
3280 - (NSString *) backButtonTitle {
3281 return @"Files";
3282 }
3283
3284 @end
3285 /* }}} */
3286 /* Package View {{{ */
3287 @interface PackageView : BrowserView {
3288 Package *package_;
3289 NSString *name_;
3290 NSMutableArray *buttons_;
3291 }
3292
3293 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3294 - (void) setPackage:(Package *)package;
3295
3296 @end
3297
3298 @implementation PackageView
3299
3300 - (void) dealloc {
3301 if (package_ != nil)
3302 [package_ release];
3303 if (name_ != nil)
3304 [name_ release];
3305 [buttons_ release];
3306 [super dealloc];
3307 }
3308
3309 - (void) _clickButtonWithName:(NSString *)name {
3310 if ([name isEqualToString:@"Install"])
3311 [delegate_ installPackage:package_];
3312 else if ([name isEqualToString:@"Reinstall"])
3313 [delegate_ installPackage:package_];
3314 else if ([name isEqualToString:@"Remove"])
3315 [delegate_ removePackage:package_];
3316 else if ([name isEqualToString:@"Upgrade"])
3317 [delegate_ installPackage:package_];
3318 else _assert(false);
3319 }
3320
3321 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3322 int count = [buttons_ count];
3323 _assert(count != 0);
3324 _assert(button <= count + 1);
3325
3326 if (count != button - 1)
3327 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3328
3329 [sheet dismiss];
3330 }
3331
3332 #include "internals.h"
3333
3334 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3335 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3336 return [super webView:sender didFinishLoadForFrame:frame];
3337 }
3338
3339 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3340 [window setValue:package_ forKey:@"package"];
3341 }
3342
3343 - (void) _rightButtonClicked {
3344 /*[super _rightButtonClicked];
3345 return;*/
3346
3347 int count = [buttons_ count];
3348 _assert(count != 0);
3349
3350 if (count == 1)
3351 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3352 else {
3353 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3354 [buttons addObjectsFromArray:buttons_];
3355 [buttons addObject:@"Cancel"];
3356
3357 [delegate_ slideUp:[[[UIActionSheet alloc]
3358 initWithTitle:nil
3359 buttons:buttons
3360 defaultButtonIndex:2
3361 delegate:self
3362 context:@"manage"
3363 ] autorelease]];
3364 }
3365 }
3366
3367 - (NSString *) _rightButtonTitle {
3368 int count = [buttons_ count];
3369 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3370 }
3371
3372 - (NSString *) title {
3373 return @"Details";
3374 }
3375
3376 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3377 if ((self = [super initWithBook:book database:database]) != nil) {
3378 database_ = database;
3379 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3380 } return self;
3381 }
3382
3383 - (void) setPackage:(Package *)package {
3384 if (package_ != nil) {
3385 [package_ autorelease];
3386 package_ = nil;
3387 }
3388
3389 if (name_ != nil) {
3390 [name_ release];
3391 name_ = nil;
3392 }
3393
3394 [buttons_ removeAllObjects];
3395
3396 if (package != nil) {
3397 package_ = [package retain];
3398 name_ = [[package id] retain];
3399
3400 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3401
3402 if ([package_ source] == nil);
3403 else if ([package_ upgradableAndEssential:NO])
3404 [buttons_ addObject:@"Upgrade"];
3405 else if ([package_ installed] == nil)
3406 [buttons_ addObject:@"Install"];
3407 else
3408 [buttons_ addObject:@"Reinstall"];
3409 if ([package_ installed] != nil)
3410 [buttons_ addObject:@"Remove"];
3411 }
3412 }
3413
3414 - (void) reloadData {
3415 [self setPackage:[database_ packageWithName:name_]];
3416 [self reloadButtons];
3417 }
3418
3419 @end
3420 /* }}} */
3421 /* Package Table {{{ */
3422 @interface PackageTable : RVPage {
3423 _transient Database *database_;
3424 NSString *title_;
3425 SEL filter_;
3426 id object_;
3427 NSMutableArray *packages_;
3428 NSMutableArray *sections_;
3429 UISectionList *list_;
3430 }
3431
3432 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3433
3434 - (void) setDelegate:(id)delegate;
3435 - (void) setObject:(id)object;
3436
3437 - (void) reloadData;
3438 - (void) resetCursor;
3439
3440 - (UISectionList *) list;
3441
3442 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3443
3444 @end
3445
3446 @implementation PackageTable
3447
3448 - (void) dealloc {
3449 [list_ setDataSource:nil];
3450
3451 [title_ release];
3452 if (object_ != nil)
3453 [object_ release];
3454 [packages_ release];
3455 [sections_ release];
3456 [list_ release];
3457 [super dealloc];
3458 }
3459
3460 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3461 return [sections_ count];
3462 }
3463
3464 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3465 return [[sections_ objectAtIndex:section] name];
3466 }
3467
3468 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3469 return [[sections_ objectAtIndex:section] row];
3470 }
3471
3472 - (int) numberOfRowsInTable:(UITable *)table {
3473 return [packages_ count];
3474 }
3475
3476 - (float) table:(UITable *)table heightForRow:(int)row {
3477 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3478 }
3479
3480 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3481 if (reusing == nil)
3482 reusing = [[[PackageCell alloc] init] autorelease];
3483 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3484 return reusing;
3485 }
3486
3487 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3488 return NO;
3489 }
3490
3491 - (void) tableRowSelected:(NSNotification *)notification {
3492 int row = [[notification object] selectedRow];
3493 if (row == INT_MAX)
3494 return;
3495
3496 Package *package = [packages_ objectAtIndex:row];
3497 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3498 [view setDelegate:delegate_];
3499 [view setPackage:package];
3500 [book_ pushPage:view];
3501 }
3502
3503 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3504 if ((self = [super initWithBook:book]) != nil) {
3505 database_ = database;
3506 title_ = [title retain];
3507 filter_ = filter;
3508 object_ = object == nil ? nil : [object retain];
3509
3510 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3511 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3512
3513 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3514 [list_ setDataSource:self];
3515
3516 UITableColumn *column = [[[UITableColumn alloc]
3517 initWithTitle:@"Name"
3518 identifier:@"name"
3519 width:[self frame].size.width
3520 ] autorelease];
3521
3522 UITable *table = [list_ table];
3523 [table setSeparatorStyle:1];
3524 [table addTableColumn:column];
3525 [table setDelegate:self];
3526 [table setReusesTableCells:YES];
3527
3528 [self addSubview:list_];
3529 [self reloadData];
3530
3531 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3532 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3533 } return self;
3534 }
3535
3536 - (void) setDelegate:(id)delegate {
3537 delegate_ = delegate;
3538 }
3539
3540 - (void) setObject:(id)object {
3541 if (object_ != nil)
3542 [object_ release];
3543 if (object == nil)
3544 object_ = nil;
3545 else
3546 object_ = [object retain];
3547 }
3548
3549 - (void) reloadData {
3550 NSArray *packages = [database_ packages];
3551
3552 [packages_ removeAllObjects];
3553 [sections_ removeAllObjects];
3554
3555 for (size_t i(0); i != [packages count]; ++i) {
3556 Package *package([packages objectAtIndex:i]);
3557 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
3558 [packages_ addObject:package];
3559 }
3560
3561 Section *section = nil;
3562
3563 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3564 Package *package = [packages_ objectAtIndex:offset];
3565 NSString *name = [package index];
3566
3567 if (section == nil || ![[section name] isEqualToString:name]) {
3568 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3569 [sections_ addObject:section];
3570 }
3571
3572 [section addToCount];
3573 }
3574
3575 [list_ reloadData];
3576 }
3577
3578 - (NSString *) title {
3579 return title_;
3580 }
3581
3582 - (void) resetViewAnimated:(BOOL)animated {
3583 [list_ resetViewAnimated:animated];
3584 }
3585
3586 - (void) resetCursor {
3587 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3588 }
3589
3590 - (UISectionList *) list {
3591 return list_;
3592 }
3593
3594 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3595 [list_ setShouldHideHeaderInShortLists:hide];
3596 }
3597
3598 @end
3599 /* }}} */
3600
3601 /* Add Source View {{{ */
3602 @interface AddSourceView : RVPage {
3603 _transient Database *database_;
3604 }
3605
3606 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3607
3608 @end
3609
3610 @implementation AddSourceView
3611
3612 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3613 if ((self = [super initWithBook:book]) != nil) {
3614 database_ = database;
3615 } return self;
3616 }
3617
3618 @end
3619 /* }}} */
3620 /* Source Cell {{{ */
3621 @interface SourceCell : UITableCell {
3622 UIImage *icon_;
3623 NSString *origin_;
3624 NSString *description_;
3625 NSString *label_;
3626 }
3627
3628 - (void) dealloc;
3629
3630 - (SourceCell *) initWithSource:(Source *)source;
3631
3632 @end
3633
3634 @implementation SourceCell
3635
3636 - (void) dealloc {
3637 [icon_ release];
3638 [origin_ release];
3639 [description_ release];
3640 [label_ release];
3641 [super dealloc];
3642 }
3643
3644 - (SourceCell *) initWithSource:(Source *)source {
3645 if ((self = [super init]) != nil) {
3646 if (icon_ == nil)
3647 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3648 if (icon_ == nil)
3649 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3650 icon_ = [icon_ retain];
3651
3652 origin_ = [[source name] retain];
3653 label_ = [[source uri] retain];
3654 description_ = [[source description] retain];
3655 } return self;
3656 }
3657
3658 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3659 if (icon_ != nil)
3660 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3661
3662 if (selected)
3663 UISetColor(White_);
3664
3665 if (!selected)
3666 UISetColor(Black_);
3667 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3668
3669 if (!selected)
3670 UISetColor(Blue_);
3671 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3672
3673 if (!selected)
3674 UISetColor(Gray_);
3675 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3676
3677 [super drawContentInRect:rect selected:selected];
3678 }
3679
3680 @end
3681 /* }}} */
3682 /* Source Table {{{ */
3683 @interface SourceTable : RVPage {
3684 _transient Database *database_;
3685 UISectionList *list_;
3686 NSMutableArray *sources_;
3687 UIActionSheet *alert_;
3688 int offset_;
3689
3690 NSString *href_;
3691 UIProgressHUD *hud_;
3692 NSError *error_;
3693
3694 //NSURLConnection *installer_;
3695 NSURLConnection *trivial_bz2_;
3696 NSURLConnection *trivial_gz_;
3697 //NSURLConnection *automatic_;
3698
3699 BOOL trivial_;
3700 }
3701
3702 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3703
3704 @end
3705
3706 @implementation SourceTable
3707
3708 - (void) _deallocConnection:(NSURLConnection *)connection {
3709 if (connection != nil) {
3710 [connection cancel];
3711 //[connection setDelegate:nil];
3712 [connection release];
3713 }
3714 }
3715
3716 - (void) dealloc {
3717 [[list_ table] setDelegate:nil];
3718 [list_ setDataSource:nil];
3719
3720 if (href_ != nil)
3721 [href_ release];
3722 if (hud_ != nil)
3723 [hud_ release];
3724 if (error_ != nil)
3725 [error_ release];
3726
3727 //[self _deallocConnection:installer_];
3728 [self _deallocConnection:trivial_gz_];
3729 [self _deallocConnection:trivial_bz2_];
3730 //[self _deallocConnection:automatic_];
3731
3732 [sources_ release];
3733 [list_ release];
3734 [super dealloc];
3735 }
3736
3737 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3738 return offset_ == 0 ? 1 : 2;
3739 }
3740
3741 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3742 switch (section + (offset_ == 0 ? 1 : 0)) {
3743 case 0: return @"Entered by User";
3744 case 1: return @"Installed by Packages";
3745
3746 default:
3747 _assert(false);
3748 return nil;
3749 }
3750 }
3751
3752 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3753 switch (section + (offset_ == 0 ? 1 : 0)) {
3754 case 0: return 0;
3755 case 1: return offset_;
3756
3757 default:
3758 _assert(false);
3759 return -1;
3760 }
3761 }
3762
3763 - (int) numberOfRowsInTable:(UITable *)table {
3764 return [sources_ count];
3765 }
3766
3767 - (float) table:(UITable *)table heightForRow:(int)row {
3768 Source *source = [sources_ objectAtIndex:row];
3769 return [source description] == nil ? 56 : 73;
3770 }
3771
3772 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3773 Source *source = [sources_ objectAtIndex:row];
3774 // XXX: weird warning, stupid selectors ;P
3775 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3776 }
3777
3778 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3779 return YES;
3780 }
3781
3782 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3783 return YES;
3784 }
3785
3786 - (void) tableRowSelected:(NSNotification*)notification {
3787 UITable *table([list_ table]);
3788 int row([table selectedRow]);
3789 if (row == INT_MAX)
3790 return;
3791
3792 Source *source = [sources_ objectAtIndex:row];
3793
3794 PackageTable *packages = [[[PackageTable alloc]
3795 initWithBook:book_
3796 database:database_
3797 title:[source label]
3798 filter:@selector(isVisibleInSource:)
3799 with:source
3800 ] autorelease];
3801
3802 [packages setDelegate:delegate_];
3803
3804 [book_ pushPage:packages];
3805 }
3806
3807 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3808 Source *source = [sources_ objectAtIndex:row];
3809 return [source record] != nil;
3810 }
3811
3812 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3813 [[list_ table] setDeleteConfirmationRow:row];
3814 }
3815
3816 - (void) table:(UITable *)table deleteRow:(int)row {
3817 Source *source = [sources_ objectAtIndex:row];
3818 [Sources_ removeObjectForKey:[source key]];
3819 [delegate_ syncData];
3820 }
3821
3822 - (void) _endConnection:(NSURLConnection *)connection {
3823 NSURLConnection **field = NULL;
3824 if (connection == trivial_bz2_)
3825 field = &trivial_bz2_;
3826 else if (connection == trivial_gz_)
3827 field = &trivial_gz_;
3828 _assert(field != NULL);
3829 [connection release];
3830 *field = nil;
3831
3832 if (
3833 trivial_bz2_ == nil &&
3834 trivial_gz_ == nil
3835 ) {
3836 [delegate_ setStatusBarShowsProgress:NO];
3837
3838 [hud_ show:NO];
3839 [hud_ removeFromSuperview];
3840 [hud_ autorelease];
3841 hud_ = nil;
3842
3843 if (trivial_) {
3844 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3845 @"deb", @"Type",
3846 href_, @"URI",
3847 @"./", @"Distribution",
3848 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3849
3850 [delegate_ syncData];
3851 } else if (error_ != nil) {
3852 UIActionSheet *sheet = [[[UIActionSheet alloc]
3853 initWithTitle:@"Verification Error"
3854 buttons:[NSArray arrayWithObjects:@"OK", nil]
3855 defaultButtonIndex:0
3856 delegate:self
3857 context:@"urlerror"
3858 ] autorelease];
3859
3860 [sheet setBodyText:[error_ localizedDescription]];
3861 [sheet popupAlertAnimated:YES];
3862 } else {
3863 UIActionSheet *sheet = [[[UIActionSheet alloc]
3864 initWithTitle:@"Did not Find Repository"
3865 buttons:[NSArray arrayWithObjects:@"OK", nil]
3866 defaultButtonIndex:0
3867 delegate:self
3868 context:@"trivial"
3869 ] autorelease];
3870
3871 [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."];
3872 [sheet popupAlertAnimated:YES];
3873 }
3874
3875 [href_ release];
3876 href_ = nil;
3877
3878 if (error_ != nil) {
3879 [error_ release];
3880 error_ = nil;
3881 }
3882 }
3883 }
3884
3885 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3886 switch ([response statusCode]) {
3887 case 200:
3888 trivial_ = YES;
3889 }
3890 }
3891
3892 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3893 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3894 if (error_ != nil)
3895 error_ = [error retain];
3896 [self _endConnection:connection];
3897 }
3898
3899 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3900 [self _endConnection:connection];
3901 }
3902
3903 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
3904 NSMutableURLRequest *request = [NSMutableURLRequest
3905 requestWithURL:[NSURL URLWithString:href]
3906 cachePolicy:NSURLRequestUseProtocolCachePolicy
3907 timeoutInterval:20.0
3908 ];
3909
3910 [request setHTTPMethod:method];
3911
3912 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
3913 }
3914
3915 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3916 NSString *context = [sheet context];
3917 if ([context isEqualToString:@"source"])
3918 switch (button) {
3919 case 1: {
3920 NSString *href = [[sheet textField] text];
3921
3922 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
3923
3924 if (![href hasSuffix:@"/"])
3925 href_ = [href stringByAppendingString:@"/"];
3926 else
3927 href_ = href;
3928 href_ = [href_ retain];
3929
3930 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
3931 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
3932 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
3933
3934 trivial_ = false;
3935
3936 hud_ = [delegate_ addProgressHUD];
3937 [hud_ setText:@"Verifying URL"];
3938 } break;
3939
3940 case 2:
3941 break;
3942
3943 default:
3944 _assert(false);
3945 }
3946
3947 [sheet dismiss];
3948 }
3949
3950 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3951 if ((self = [super initWithBook:book]) != nil) {
3952 database_ = database;
3953 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
3954
3955 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
3956 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3957 [list_ setShouldHideHeaderInShortLists:NO];
3958
3959 [self addSubview:list_];
3960 [list_ setDataSource:self];
3961
3962 UITableColumn *column = [[UITableColumn alloc]
3963 initWithTitle:@"Name"
3964 identifier:@"name"
3965 width:[self frame].size.width
3966 ];
3967
3968 UITable *table = [list_ table];
3969 [table setSeparatorStyle:1];
3970 [table addTableColumn:column];
3971 [table setDelegate:self];
3972
3973 [self reloadData];
3974
3975 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3976 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3977 } return self;
3978 }
3979
3980 - (void) reloadData {
3981 pkgSourceList list;
3982 _assert(list.ReadMainList());
3983
3984 [sources_ removeAllObjects];
3985 [sources_ addObjectsFromArray:[database_ sources]];
3986 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
3987
3988 int count = [sources_ count];
3989 for (offset_ = 0; offset_ != count; ++offset_) {
3990 Source *source = [sources_ objectAtIndex:offset_];
3991 if ([source record] == nil)
3992 break;
3993 }
3994
3995 [list_ reloadData];
3996 }
3997
3998 - (void) resetViewAnimated:(BOOL)animated {
3999 [list_ resetViewAnimated:animated];
4000 }
4001
4002 - (void) _leftButtonClicked {
4003 /*[book_ pushPage:[[[AddSourceView alloc]
4004 initWithBook:book_
4005 database:database_
4006 ] autorelease]];*/
4007
4008 UIActionSheet *sheet = [[[UIActionSheet alloc]
4009 initWithTitle:@"Enter Cydia/APT URL"
4010 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4011 defaultButtonIndex:0
4012 delegate:self
4013 context:@"source"
4014 ] autorelease];
4015
4016 [sheet addTextFieldWithValue:@"http://" label:@""];
4017
4018 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4019 [traits setAutocapitalizationType:0];
4020 [traits setKeyboardType:3];
4021 [traits setAutocorrectionType:1];
4022
4023 [sheet popupAlertAnimated:YES];
4024 }
4025
4026 - (void) _rightButtonClicked {
4027 UITable *table = [list_ table];
4028 BOOL editing = [table isRowDeletionEnabled];
4029 [table enableRowDeletion:!editing animated:YES];
4030 [book_ reloadButtonsForPage:self];
4031 }
4032
4033 - (NSString *) title {
4034 return @"Sources";
4035 }
4036
4037 - (NSString *) backButtonTitle {
4038 return @"Sources";
4039 }
4040
4041 - (NSString *) leftButtonTitle {
4042 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4043 }
4044
4045 - (NSString *) rightButtonTitle {
4046 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4047 }
4048
4049 - (UINavigationButtonStyle) rightButtonStyle {
4050 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4051 }
4052
4053 @end
4054 /* }}} */
4055
4056 /* Installed View {{{ */
4057 @interface InstalledView : RVPage {
4058 _transient Database *database_;
4059 PackageTable *packages_;
4060 BOOL expert_;
4061 }
4062
4063 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4064
4065 @end
4066
4067 @implementation InstalledView
4068
4069 - (void) dealloc {
4070 [packages_ release];
4071 [super dealloc];
4072 }
4073
4074 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4075 if ((self = [super initWithBook:book]) != nil) {
4076 database_ = database;
4077
4078 packages_ = [[PackageTable alloc]
4079 initWithBook:book
4080 database:database
4081 title:nil
4082 filter:@selector(isInstalledAndVisible:)
4083 with:[NSNumber numberWithBool:YES]
4084 ];
4085
4086 [self addSubview:packages_];
4087
4088 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4089 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4090 } return self;
4091 }
4092
4093 - (void) resetViewAnimated:(BOOL)animated {
4094 [packages_ resetViewAnimated:animated];
4095 }
4096
4097 - (void) reloadData {
4098 [packages_ reloadData];
4099 }
4100
4101 - (void) _rightButtonClicked {
4102 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4103 [packages_ reloadData];
4104 expert_ = !expert_;
4105 [book_ reloadButtonsForPage:self];
4106 }
4107
4108 - (NSString *) title {
4109 return @"Installed";
4110 }
4111
4112 - (NSString *) backButtonTitle {
4113 return @"Packages";
4114 }
4115
4116 - (NSString *) rightButtonTitle {
4117 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4118 }
4119
4120 - (UINavigationButtonStyle) rightButtonStyle {
4121 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4122 }
4123
4124 - (void) setDelegate:(id)delegate {
4125 [super setDelegate:delegate];
4126 [packages_ setDelegate:delegate];
4127 }
4128
4129 @end
4130 /* }}} */
4131
4132 /* Home View {{{ */
4133 @interface HomeView : BrowserView {
4134 }
4135
4136 @end
4137
4138 @implementation HomeView
4139
4140 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4141 [sheet dismiss];
4142 }
4143
4144 - (void) _leftButtonClicked {
4145 UIActionSheet *sheet = [[[UIActionSheet alloc]
4146 initWithTitle:@"About Cydia Installer"
4147 buttons:[NSArray arrayWithObjects:@"Close", nil]
4148 defaultButtonIndex:0
4149 delegate:self
4150 context:@"about"
4151 ] autorelease];
4152
4153 [sheet setBodyText:
4154 @"Copyright (C) 2008\n"
4155 "Jay Freeman (saurik)\n"
4156 "saurik@saurik.com\n"
4157 "http://www.saurik.com/\n"
4158 "\n"
4159 "The Okori Group\n"
4160 "http://www.theokorigroup.com/\n"
4161 "\n"
4162 "College of Creative Studies,\n"
4163 "University of California,\n"
4164 "Santa Barbara\n"
4165 "http://www.ccs.ucsb.edu/"
4166 ];
4167
4168 [sheet popupAlertAnimated:YES];
4169 }
4170
4171 - (NSString *) leftButtonTitle {
4172 return @"About";
4173 }
4174
4175 @end
4176 /* }}} */
4177 /* Manage View {{{ */
4178 @interface ManageView : BrowserView {
4179 }
4180
4181 @end
4182
4183 @implementation ManageView
4184
4185 - (NSString *) title {
4186 return @"Manage";
4187 }
4188
4189 - (void) _leftButtonClicked {
4190 [delegate_ askForSettings];
4191 }
4192
4193 - (NSString *) leftButtonTitle {
4194 return @"Settings";
4195 }
4196
4197 - (NSString *) _rightButtonTitle {
4198 return nil;
4199 }
4200
4201 @end
4202 /* }}} */
4203
4204 /* Indirect Delegate {{{ */
4205 @interface IndirectDelegate : NSProxy {
4206 _transient id delegate_;
4207 }
4208
4209 - (void) setDelegate:(id)delegate;
4210 - (id) initWithDelegate:(id)delegate;
4211 @end
4212
4213 @implementation IndirectDelegate
4214
4215 - (void) setDelegate:(id)delegate {
4216 delegate_ = delegate;
4217 }
4218
4219 - (id) initWithDelegate:(id)delegate {
4220 delegate_ = delegate;
4221 return self;
4222 }
4223
4224 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4225 if (delegate_ != nil)
4226 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4227 return sig;
4228 return nil;
4229 }
4230
4231 - (void) forwardInvocation:(NSInvocation*)inv {
4232 SEL sel = [inv selector];
4233 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4234 [inv invokeWithTarget:delegate_];
4235 }
4236
4237 @end
4238 /* }}} */
4239 /* Browser Implementation {{{ */
4240 @implementation BrowserView
4241
4242 - (void) dealloc {
4243 WebView *webview = [webview_ webView];
4244 [webview setFrameLoadDelegate:nil];
4245 [webview setResourceLoadDelegate:nil];
4246 [webview setUIDelegate:nil];
4247
4248 [webview_ setDelegate:nil];
4249 [webview_ setGestureDelegate:nil];
4250
4251 /*WebFrame *frame = [webview mainFrame];
4252 [frame loadHTMLString:@"" baseURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];*/
4253
4254 //[webview_ removeFromSuperview];
4255 //[Documents_ addObject:[webview_ autorelease]];
4256 [webview_ release];
4257
4258 [indirect_ setDelegate:nil];
4259 [indirect_ release];
4260
4261 [scroller_ setDelegate:nil];
4262
4263 [scroller_ release];
4264 [urls_ release];
4265 [indicator_ release];
4266 if (title_ != nil)
4267 [title_ release];
4268 [super dealloc];
4269 }
4270
4271 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4272 [self loadRequest:[NSURLRequest
4273 requestWithURL:url
4274 cachePolicy:policy
4275 timeoutInterval:30.0
4276 ]];
4277 }
4278
4279 - (void) loadURL:(NSURL *)url {
4280 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4281 }
4282
4283 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4284 NSMutableURLRequest *copy = [request mutableCopy];
4285
4286 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4287 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4288 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4289
4290 if (Role_ != nil)
4291 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4292
4293 return copy;
4294 }
4295
4296 - (void) loadRequest:(NSURLRequest *)request {
4297 pushed_ = true;
4298 [webview_ loadRequest:request];
4299 }
4300
4301 - (void) reloadURL {
4302 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4303 [urls_ removeLastObject];
4304 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4305 }
4306
4307 - (WebView *) webView {
4308 return [webview_ webView];
4309 }
4310
4311 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4312 [scroller_ setContentSize:frame.size];
4313 }
4314
4315 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4316 [self view:sender didSetFrame:frame];
4317 }
4318
4319 - (void) pushPage:(RVPage *)page {
4320 [self setBackButtonTitle:title_];
4321 [page setDelegate:delegate_];
4322 [book_ pushPage:page];
4323 }
4324
4325 - (RVPage *) _pageForPackage:(NSString *)name {
4326 if (Package *package = [database_ packageWithName:name]) {
4327 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4328 [view setPackage:package];
4329 return view;
4330 } else {
4331 UIActionSheet *sheet = [[[UIActionSheet alloc]
4332 initWithTitle:@"Cannot Locate Package"
4333 buttons:[NSArray arrayWithObjects:@"Close", nil]
4334 defaultButtonIndex:0
4335 delegate:self
4336 context:@"missing"
4337 ] autorelease];
4338
4339 [sheet setBodyText:[NSString stringWithFormat:
4340 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4341 , name]];
4342
4343 [sheet popupAlertAnimated:YES];
4344 return nil;
4345 }
4346 }
4347
4348 - (BOOL) getSpecial:(NSString *)href {
4349 RVPage *page = nil;
4350
4351 if (
4352 [href hasPrefix:@"http://ax.phobos.apple.com/"] ||
4353 [href hasPrefix:@"http://phobos.apple.com/"] ||
4354 [href hasPrefix:@"http://www.youtube.com/watch?"] ||
4355 [href hasPrefix:@"mailto:"] ||
4356 [href hasPrefix:@"tel:"]
4357 )
4358 [delegate_ openURL:[NSURL URLWithString:href]];
4359 else if ([href isEqualToString:@"cydia://add-source"])
4360 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4361 else if ([href isEqualToString:@"cydia://sources"])
4362 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4363 else if ([href isEqualToString:@"cydia://packages"])
4364 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4365 else if ([href hasPrefix:@"cydia://files/"]) {
4366 NSString *name = [href substringFromIndex:14];
4367
4368 if (Package *package = [database_ packageWithName:name]) {
4369 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
4370 [files setPackage:package];
4371 page = files;
4372 }
4373 } else if ([href hasPrefix:@"apptapp://package/"])
4374 page = [self _pageForPackage:[href substringFromIndex:18]];
4375 else if ([href hasPrefix:@"cydia://package/"])
4376 page = [self _pageForPackage:[href substringFromIndex:16]];
4377 else if (![href hasPrefix:@"apptapp:"] && ![href hasPrefix:@"cydia:"])
4378 return false;
4379
4380 if (page != nil)
4381 [self pushPage:page];
4382 return true;
4383 }
4384
4385 - (void) webView:(WebView *)sender willClickElement:(id)element {
4386 if ([[element localName] isEqualToString:@"img"])
4387 do if ((element = [element parentNode]) == nil)
4388 return;
4389 while (![[element localName] isEqualToString:@"a"]);
4390 if (![element respondsToSelector:@selector(href)])
4391 return;
4392 NSString *href = [element href];
4393 if (href == nil)
4394 return;
4395 [self getSpecial:href];
4396 }
4397
4398 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4399 NSURL *url = [request URL];
4400 if ([self getSpecial:[url absoluteString]])
4401 return nil;
4402
4403 if (!pushed_) {
4404 pushed_ = true;
4405 [book_ pushPage:self];
4406 }
4407
4408 return [self _addHeadersToRequest:request];
4409 }
4410
4411 - (BOOL) isSpecialScheme:(NSString *)scheme {
4412 return
4413 [scheme isEqualToString:@"apptapp"] ||
4414 [scheme isEqualToString:@"cydia"] ||
4415 [scheme isEqualToString:@"mailto"] ||
4416 [scheme isEqualToString:@"tel"];
4417 }
4418
4419 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4420 if (request != nil) {
4421 NSURL *url = [request URL];
4422 NSString *scheme = [url scheme];
4423 NSString *absolute = [url absoluteString];
4424 if (
4425 [self isSpecialScheme:scheme] ||
4426 [absolute hasPrefix:@"http://ax.phobos.apple.com/"] ||
4427 [absolute hasPrefix:@"http://phobos.apple.com/"] ||
4428 [absolute hasPrefix:@"http://www.yahoo.com/watch?"]
4429 )
4430 return nil;
4431 }
4432
4433 [self setBackButtonTitle:title_];
4434
4435 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4436 [browser setDelegate:delegate_];
4437
4438 if (request != nil) {
4439 [browser loadRequest:[self _addHeadersToRequest:request]];
4440 [book_ pushPage:browser];
4441 }
4442
4443 return [browser webView];
4444 }
4445
4446 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4447 if ([frame parentFrame] != nil)
4448 return;
4449
4450 title_ = [title retain];
4451 [self setTitle:title];
4452 }
4453
4454 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4455 if ([frame parentFrame] != nil)
4456 return;
4457
4458 reloading_ = false;
4459 loading_ = true;
4460 [indicator_ startAnimation];
4461 [self reloadButtons];
4462
4463 if (title_ != nil) {
4464 [title_ release];
4465 title_ = nil;
4466 }
4467
4468 [self setTitle:@"Loading"];
4469
4470 WebView *webview = [webview_ webView];
4471 NSString *href = [webview mainFrameURL];
4472 [urls_ addObject:[NSURL URLWithString:href]];
4473
4474 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4475
4476 CGRect webrect = [scroller_ bounds];
4477 webrect.size.height = 0;
4478 [webview_ setFrame:webrect];
4479 }
4480
4481 - (void) _finishLoading {
4482 if (!reloading_) {
4483 loading_ = false;
4484 [indicator_ stopAnimation];
4485 [self reloadButtons];
4486 }
4487 }
4488
4489 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
4490 _trace();
4491 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4492 }
4493
4494 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4495 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4496 }
4497
4498 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4499 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4500 }
4501
4502 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4503 return [webview_ webView:sender didCommitLoadForFrame:frame];
4504 }
4505
4506 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4507 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4508 }
4509
4510 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4511 if ([frame parentFrame] == nil)
4512 [self _finishLoading];
4513 return [webview_ webView:sender didFinishLoadForFrame:frame];
4514 }
4515
4516 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4517 if ([frame parentFrame] != nil)
4518 return;
4519 [self _finishLoading];
4520
4521 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4522 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4523 [[error localizedDescription] stringByAddingPercentEscapes]
4524 ]]];
4525 }
4526
4527 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4528 if ((self = [super initWithBook:book]) != nil) {
4529 database_ = database;
4530 loading_ = false;
4531
4532 struct CGRect bounds = [self bounds];
4533
4534 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4535 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4536 [self addSubview:pinstripe];
4537
4538 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4539 [self addSubview:scroller_];
4540
4541 [scroller_ setScrollingEnabled:YES];
4542 [scroller_ setAdjustForContentSizeChange:YES];
4543 [scroller_ setClipsSubviews:YES];
4544 [scroller_ setAllowsRubberBanding:YES];
4545 [scroller_ setScrollDecelerationFactor:0.99];
4546 [scroller_ setDelegate:self];
4547
4548 CGRect webrect = [scroller_ bounds];
4549 webrect.size.height = 0;
4550
4551 webview_ = [Documents_ lastObject];
4552 if (webview_ != nil) {
4553 webview_ = [webview_ retain];
4554 [Documents_ removeLastObject];
4555 [webview_ setFrame:webrect];
4556 } else {
4557 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
4558
4559 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4560
4561 [webview_ setTilingEnabled:YES];
4562 [webview_ setTileMinificationFilter:kCAFilterNearest];
4563 [webview_ setAutoresizes:YES];
4564
4565 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
4566 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
4567 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
4568
4569 [webview_ _setDocumentType:0x4];
4570
4571 [webview_ setZoomsFocusedFormControl:YES];
4572 [webview_ setContentsPosition:7];
4573 [webview_ setEnabledGestures:0xa];
4574 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
4575 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
4576 [webview_ setSmoothsFonts:YES];
4577 }
4578
4579 [webview_ setDelegate:self];
4580 [webview_ setGestureDelegate:self];
4581 [scroller_ addSubview:webview_];
4582
4583 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4584 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
4585 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4586
4587 Package *package([database_ packageWithName:@"cydia"]);
4588 NSString *application = package == nil ? @"Cydia" : [NSString
4589 stringWithFormat:@"Cydia/%@",
4590 [package installed]
4591 ];
4592
4593 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
4594
4595 WebView *webview = [webview_ webView];
4596 [webview setApplicationNameForUserAgent:application];
4597 [webview setFrameLoadDelegate:self];
4598 [webview setResourceLoadDelegate:indirect_];
4599 [webview setUIDelegate:self];
4600
4601 //[webview _setLayoutInterval:0.5];
4602
4603 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4604
4605 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4606 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4607 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4608 } return self;
4609 }
4610
4611 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
4612 [webview_ redrawScaledDocument];
4613 }
4614
4615 - (void) _rightButtonClicked {
4616 reloading_ = true;
4617 [self reloadURL];
4618 }
4619
4620 - (NSString *) _rightButtonTitle {
4621 return @"Reload";
4622 }
4623
4624 - (NSString *) rightButtonTitle {
4625 return loading_ ? @"" : [self _rightButtonTitle];
4626 }
4627
4628 - (NSString *) title {
4629 return nil;
4630 }
4631
4632 - (NSString *) backButtonTitle {
4633 return @"Browser";
4634 }
4635
4636 - (void) setPageActive:(BOOL)active {
4637 if (!active)
4638 [indicator_ removeFromSuperview];
4639 else
4640 [[book_ navigationBar] addSubview:indicator_];
4641 }
4642
4643 - (void) resetViewAnimated:(BOOL)animated {
4644 }
4645
4646 - (void) setPushed:(bool)pushed {
4647 pushed_ = pushed;
4648 }
4649
4650 @end
4651 /* }}} */
4652
4653 @interface CYBook : RVBook <
4654 ProgressDelegate
4655 > {
4656 _transient Database *database_;
4657 UINavigationBar *overlay_;
4658 UIProgressIndicator *indicator_;
4659 UITextLabel *prompt_;
4660 UIProgressBar *progress_;
4661 UINavigationButton *cancel_;
4662 bool updating_;
4663 size_t received_;
4664 NSTimeInterval last_;
4665 }
4666
4667 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4668 - (void) update;
4669 - (BOOL) updating;
4670
4671 @end
4672
4673 /* Install View {{{ */
4674 @interface InstallView : RVPage {
4675 _transient Database *database_;
4676 NSMutableArray *sections_;
4677 NSMutableArray *filtered_;
4678 UITransitionView *transition_;
4679 UITable *list_;
4680 UIView *accessory_;
4681 BOOL editing_;
4682 }
4683
4684 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4685 - (void) reloadData;
4686 - (void) resetView;
4687
4688 @end
4689
4690 @implementation InstallView
4691
4692 - (void) dealloc {
4693 [list_ setDataSource:nil];
4694 [list_ setDelegate:nil];
4695
4696 [sections_ release];
4697 [filtered_ release];
4698 [transition_ release];
4699 [list_ release];
4700 [accessory_ release];
4701 [super dealloc];
4702 }
4703
4704 - (int) numberOfRowsInTable:(UITable *)table {
4705 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4706 }
4707
4708 - (float) table:(UITable *)table heightForRow:(int)row {
4709 return 45;
4710 }
4711
4712 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4713 if (reusing == nil)
4714 reusing = [[[SectionCell alloc] init] autorelease];
4715 [(SectionCell *)reusing setSection:(editing_ ?
4716 [sections_ objectAtIndex:row] :
4717 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4718 ) editing:editing_];
4719 return reusing;
4720 }
4721
4722 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4723 return !editing_;
4724 }
4725
4726 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4727 return !editing_;
4728 }
4729
4730 - (void) tableRowSelected:(NSNotification *)notification {
4731 int row = [[notification object] selectedRow];
4732 if (row == INT_MAX)
4733 return;
4734
4735 Section *section;
4736 NSString *name;
4737 NSString *title;
4738
4739 if (row == 0) {
4740 section = nil;
4741 name = nil;
4742 title = @"All Packages";
4743 } else {
4744 section = [filtered_ objectAtIndex:(row - 1)];
4745 name = [section name];
4746
4747 if (name != nil)
4748 title = name;
4749 else {
4750 name = @"";
4751 title = @"(No Section)";
4752 }
4753 }
4754
4755 PackageTable *table = [[[PackageTable alloc]
4756 initWithBook:book_
4757 database:database_
4758 title:title
4759 filter:@selector(isVisiblyUninstalledInSection:)
4760 with:name
4761 ] autorelease];
4762
4763 [table setDelegate:delegate_];
4764
4765 [book_ pushPage:table];
4766 }
4767
4768 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4769 if ((self = [super initWithBook:book]) != nil) {
4770 database_ = database;
4771
4772 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4773 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4774
4775 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4776 [self addSubview:transition_];
4777
4778 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4779 [transition_ transition:0 toView:list_];
4780
4781 UITableColumn *column = [[[UITableColumn alloc]
4782 initWithTitle:@"Name"
4783 identifier:@"name"
4784 width:[self frame].size.width
4785 ] autorelease];
4786
4787 [list_ setDataSource:self];
4788 [list_ setSeparatorStyle:1];
4789 [list_ addTableColumn:column];
4790 [list_ setDelegate:self];
4791 [list_ setReusesTableCells:YES];
4792
4793 [self reloadData];
4794
4795 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4796 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4797 } return self;
4798 }
4799
4800 - (void) reloadData {
4801 NSArray *packages = [database_ packages];
4802
4803 [sections_ removeAllObjects];
4804 [filtered_ removeAllObjects];
4805
4806 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4807 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4808
4809 for (size_t i(0); i != [packages count]; ++i) {
4810 Package *package([packages objectAtIndex:i]);
4811 NSString *name([package section]);
4812
4813 if (name != nil) {
4814 Section *section([sections objectForKey:name]);
4815 if (section == nil) {
4816 section = [[[Section alloc] initWithName:name] autorelease];
4817 [sections setObject:section forKey:name];
4818 }
4819 }
4820
4821 if ([package valid] && [package installed] == nil && [package visible])
4822 [filtered addObject:package];
4823 }
4824
4825 [sections_ addObjectsFromArray:[sections allValues]];
4826 [sections_ sortUsingSelector:@selector(compareByName:)];
4827
4828 [filtered sortUsingSelector:@selector(compareBySection:)];
4829
4830 Section *section = nil;
4831 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4832 Package *package = [filtered objectAtIndex:offset];
4833 NSString *name = [package section];
4834
4835 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4836 section = name == nil ?
4837 [[[Section alloc] initWithName:nil] autorelease] :
4838 [sections objectForKey:name];
4839 [filtered_ addObject:section];
4840 }
4841
4842 [section addToCount];
4843 }
4844
4845 [list_ reloadData];
4846 }
4847
4848 - (void) resetView {
4849 if (editing_)
4850 [self _rightButtonClicked];
4851 }
4852
4853 - (void) resetViewAnimated:(BOOL)animated {
4854 [list_ resetViewAnimated:animated];
4855 }
4856
4857 - (void) _rightButtonClicked {
4858 if ((editing_ = !editing_))
4859 [list_ reloadData];
4860 else {
4861 [delegate_ updateData];
4862 }
4863
4864 [book_ setTitle:[self title] forPage:self];
4865 [book_ reloadButtonsForPage:self];
4866 }
4867
4868 - (NSString *) title {
4869 return editing_ ? @"Section Visibility" : @"Install by Section";
4870 }
4871
4872 - (NSString *) backButtonTitle {
4873 return @"Sections";
4874 }
4875
4876 - (NSString *) rightButtonTitle {
4877 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4878 }
4879
4880 - (UINavigationButtonStyle) rightButtonStyle {
4881 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4882 }
4883
4884 - (UIView *) accessoryView {
4885 return accessory_;
4886 }
4887
4888 @end
4889 /* }}} */
4890 /* Changes View {{{ */
4891 @interface ChangesView : RVPage {
4892 _transient Database *database_;
4893 NSMutableArray *packages_;
4894 NSMutableArray *sections_;
4895 UISectionList *list_;
4896 unsigned upgrades_;
4897 }
4898
4899 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4900 - (void) reloadData;
4901
4902 @end
4903
4904 @implementation ChangesView
4905
4906 - (void) dealloc {
4907 [[list_ table] setDelegate:nil];
4908 [list_ setDataSource:nil];
4909
4910 [packages_ release];
4911 [sections_ release];
4912 [list_ release];
4913 [super dealloc];
4914 }
4915
4916 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4917 return [sections_ count];
4918 }
4919
4920 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4921 return [[sections_ objectAtIndex:section] name];
4922 }
4923
4924 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4925 return [[sections_ objectAtIndex:section] row];
4926 }
4927
4928 - (int) numberOfRowsInTable:(UITable *)table {
4929 return [packages_ count];
4930 }
4931
4932 - (float) table:(UITable *)table heightForRow:(int)row {
4933 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4934 }
4935
4936 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4937 if (reusing == nil)
4938 reusing = [[[PackageCell alloc] init] autorelease];
4939 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4940 return reusing;
4941 }
4942
4943 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4944 return NO;
4945 }
4946
4947 - (void) tableRowSelected:(NSNotification *)notification {
4948 int row = [[notification object] selectedRow];
4949 if (row == INT_MAX)
4950 return;
4951 Package *package = [packages_ objectAtIndex:row];
4952 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4953 [view setDelegate:delegate_];
4954 [view setPackage:package];
4955 [book_ pushPage:view];
4956 }
4957
4958 - (void) _leftButtonClicked {
4959 [(CYBook *)book_ update];
4960 [self reloadButtons];
4961 }
4962
4963 - (void) _rightButtonClicked {
4964 [delegate_ distUpgrade];
4965 }
4966
4967 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4968 if ((self = [super initWithBook:book]) != nil) {
4969 database_ = database;
4970
4971 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4972 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4973
4974 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4975 [self addSubview:list_];
4976
4977 [list_ setShouldHideHeaderInShortLists:NO];
4978 [list_ setDataSource:self];
4979 //[list_ setSectionListStyle:1];
4980
4981 UITableColumn *column = [[[UITableColumn alloc]
4982 initWithTitle:@"Name"
4983 identifier:@"name"
4984 width:[self frame].size.width
4985 ] autorelease];
4986
4987 UITable *table = [list_ table];
4988 [table setSeparatorStyle:1];
4989 [table addTableColumn:column];
4990 [table setDelegate:self];
4991 [table setReusesTableCells:YES];
4992
4993 [self reloadData];
4994
4995 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4996 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4997 } return self;
4998 }
4999
5000 - (void) reloadData {
5001 NSArray *packages = [database_ packages];
5002
5003 [packages_ removeAllObjects];
5004 [sections_ removeAllObjects];
5005
5006 for (size_t i(0); i != [packages count]; ++i) {
5007 Package *package([packages objectAtIndex:i]);
5008
5009 if (
5010 [package installed] == nil && [package valid] && [package visible] ||
5011 [package upgradableAndEssential:NO]
5012 )
5013 [packages_ addObject:package];
5014 }
5015
5016 [packages_ sortUsingSelector:@selector(compareForChanges:)];
5017
5018 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5019 Section *section = nil;
5020
5021 upgrades_ = 0;
5022 bool unseens = false;
5023
5024 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5025
5026 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5027 Package *package = [packages_ objectAtIndex:offset];
5028
5029 if ([package upgradableAndEssential:YES]) {
5030 ++upgrades_;
5031 [upgradable addToCount];
5032 } else {
5033 unseens = true;
5034 NSDate *seen = [package seen];
5035
5036 NSString *name;
5037
5038 if (seen == nil)
5039 name = [@"n/a ?" retain];
5040 else {
5041 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
5042 }
5043
5044 if (section == nil || ![[section name] isEqualToString:name]) {
5045 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5046 [sections_ addObject:section];
5047 }
5048
5049 [name release];
5050 [section addToCount];
5051 }
5052 }
5053
5054 CFRelease(formatter);
5055
5056 if (unseens) {
5057 Section *last = [sections_ lastObject];
5058 size_t count = [last count];
5059 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5060 [sections_ removeLastObject];
5061 }
5062
5063 if (upgrades_ != 0)
5064 [sections_ insertObject:upgradable atIndex:0];
5065
5066 [list_ reloadData];
5067 [self reloadButtons];
5068 }
5069
5070 - (void) resetViewAnimated:(BOOL)animated {
5071 [list_ resetViewAnimated:animated];
5072 }
5073
5074 - (NSString *) leftButtonTitle {
5075 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5076 }
5077
5078 - (NSString *) rightButtonTitle {
5079 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5080 }
5081
5082 - (NSString *) title {
5083 return @"Changes";
5084 }
5085
5086 @end
5087 /* }}} */
5088 /* Search View {{{ */
5089 @protocol SearchViewDelegate
5090 - (void) showKeyboard:(BOOL)show;
5091 @end
5092
5093 @interface SearchView : RVPage {
5094 UIView *accessory_;
5095 UISearchField *field_;
5096 UITransitionView *transition_;
5097 PackageTable *table_;
5098 UIPreferencesTable *advanced_;
5099 UIView *dimmed_;
5100 bool flipped_;
5101 bool reload_;
5102 }
5103
5104 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5105 - (void) reloadData;
5106
5107 @end
5108
5109 @implementation SearchView
5110
5111 - (void) dealloc {
5112 #ifndef __OBJC2__
5113 [[field_ textTraits] setEditingDelegate:nil];
5114 #endif
5115 [field_ setDelegate:nil];
5116
5117 [accessory_ release];
5118 [field_ release];
5119 [transition_ release];
5120 [table_ release];
5121 [advanced_ release];
5122 [dimmed_ release];
5123 [super dealloc];
5124 }
5125
5126 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5127 return 1;
5128 }
5129
5130 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5131 switch (group) {
5132 case 0: return @"Advanced Search (Coming Soon!)";
5133
5134 default: _assert(false);
5135 }
5136 }
5137
5138 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5139 switch (group) {
5140 case 0: return 0;
5141
5142 default: _assert(false);
5143 }
5144 }
5145
5146 - (void) _showKeyboard:(BOOL)show {
5147 CGSize keysize = [UIKeyboard defaultSize];
5148 CGRect keydown = [book_ pageBounds];
5149 CGRect keyup = keydown;
5150 keyup.size.height -= keysize.height - ButtonBarHeight_;
5151
5152 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5153
5154 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5155 [animation setSignificantRectFields:8];
5156
5157 if (show) {
5158 [animation setStartFrame:keydown];
5159 [animation setEndFrame:keyup];
5160 } else {
5161 [animation setStartFrame:keyup];
5162 [animation setEndFrame:keydown];
5163 }
5164
5165 UIAnimator *animator = [UIAnimator sharedAnimator];
5166
5167 [animator
5168 addAnimations:[NSArray arrayWithObjects:animation, nil]
5169 withDuration:(KeyboardTime_ - delay)
5170 start:!show
5171 ];
5172
5173 if (show)
5174 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5175
5176 #ifndef __OBJC2__
5177 [delegate_ showKeyboard:show];
5178 #endif
5179 }
5180
5181 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5182 [self _showKeyboard:YES];
5183 }
5184
5185 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5186 [self _showKeyboard:NO];
5187 }
5188
5189 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5190 if (reload_) {
5191 NSString *text([field_ text]);
5192 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5193 [self reloadData];
5194 reload_ = false;
5195 }
5196 }
5197
5198 - (void) textFieldClearButtonPressed:(UITextField *)field {
5199 reload_ = true;
5200 }
5201
5202 - (void) keyboardInputShouldDelete:(id)input {
5203 reload_ = true;
5204 }
5205
5206 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5207 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5208 reload_ = true;
5209 return YES;
5210 } else {
5211 [field_ resignFirstResponder];
5212 return NO;
5213 }
5214 }
5215
5216 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5217 if ((self = [super initWithBook:book]) != nil) {
5218 CGRect pageBounds = [book_ pageBounds];
5219
5220 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5221 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5222 [self addSubview:pinstripe];*/
5223
5224 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5225 [self addSubview:transition_];
5226
5227 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5228
5229 [advanced_ setReusesTableCells:YES];
5230 [advanced_ setDataSource:self];
5231 [advanced_ reloadData];
5232
5233 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5234 CGColor dimmed(space_, 0, 0, 0, 0.5);
5235 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5236
5237 table_ = [[PackageTable alloc]
5238 initWithBook:book
5239 database:database
5240 title:nil
5241 filter:@selector(isUnfilteredAndSearchedForBy:)
5242 with:nil
5243 ];
5244
5245 [table_ setShouldHideHeaderInShortLists:NO];
5246 [transition_ transition:0 toView:table_];
5247
5248 CGRect cnfrect = {{
5249 #ifdef __OBJC2__
5250 6 +
5251 #endif
5252 1, 38}, {17, 18}};
5253
5254 CGRect area;
5255 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5256 area.origin.y = 1;
5257
5258 area.size.width =
5259 #ifdef __OBJC2__
5260 8 +
5261 #endif
5262 [self bounds].size.width - area.origin.x - 18;
5263
5264 area.size.height = [UISearchField defaultHeight];
5265
5266 field_ = [[UISearchField alloc] initWithFrame:area];
5267
5268 UIFont *font = [UIFont systemFontOfSize:16];
5269 [field_ setFont:font];
5270
5271 [field_ setPlaceholder:@"Package Names & Descriptions"];
5272 [field_ setDelegate:self];
5273
5274 [field_ setPaddingTop:3];
5275
5276 UITextInputTraits *traits = [field_ textInputTraits];
5277 [traits setAutocapitalizationType:0];
5278 [traits setAutocorrectionType:1];
5279 [traits setReturnKeyType:6];
5280
5281 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5282
5283 accessory_ = [[UIView alloc] initWithFrame:accrect];
5284 [accessory_ addSubview:field_];
5285
5286 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5287 [configure setShowPressFeedback:YES];
5288 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5289 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5290 [accessory_ addSubview:configure];*/
5291
5292 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5293 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5294 } return self;
5295 }
5296
5297 - (void) flipPage {
5298 #ifndef __OBJC2__
5299 LKAnimation *animation = [LKTransition animation];
5300 [animation setType:@"oglFlip"];
5301 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5302 [animation setFillMode:@"extended"];
5303 [animation setTransitionFlags:3];
5304 [animation setDuration:10];
5305 [animation setSpeed:0.35];
5306 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5307 [[transition_ _layer] addAnimation:animation forKey:0];
5308 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5309 flipped_ = !flipped_;
5310 #endif
5311 }
5312
5313 - (void) configurePushed {
5314 [field_ resignFirstResponder];
5315 [self flipPage];
5316 }
5317
5318 - (void) resetViewAnimated:(BOOL)animated {
5319 if (flipped_)
5320 [self flipPage];
5321 [table_ resetViewAnimated:animated];
5322 }
5323
5324 - (void) reloadData {
5325 if (flipped_)
5326 [self flipPage];
5327 [table_ setObject:[field_ text]];
5328 [table_ reloadData];
5329 [table_ resetCursor];
5330 }
5331
5332 - (UIView *) accessoryView {
5333 return accessory_;
5334 }
5335
5336 - (NSString *) title {
5337 return nil;
5338 }
5339
5340 - (NSString *) backButtonTitle {
5341 return @"Search";
5342 }
5343
5344 - (void) setDelegate:(id)delegate {
5345 [table_ setDelegate:delegate];
5346 [super setDelegate:delegate];
5347 }
5348
5349 @end
5350 /* }}} */
5351
5352 @implementation CYBook
5353
5354 - (void) dealloc {
5355 [overlay_ release];
5356 [indicator_ release];
5357 [prompt_ release];
5358 [progress_ release];
5359 [cancel_ release];
5360 [super dealloc];
5361 }
5362
5363 - (NSString *) getTitleForPage:(RVPage *)page {
5364 return Simplify([super getTitleForPage:page]);
5365 }
5366
5367 - (BOOL) updating {
5368 return updating_;
5369 }
5370
5371 - (void) update {
5372 [UIView beginAnimations:nil context:NULL];
5373
5374 CGRect ovrframe = [overlay_ frame];
5375 ovrframe.origin.y = 0;
5376 [overlay_ setFrame:ovrframe];
5377
5378 CGRect barframe = [navbar_ frame];
5379 barframe.origin.y += ovrframe.size.height;
5380 [navbar_ setFrame:barframe];
5381
5382 CGRect trnframe = [transition_ frame];
5383 trnframe.origin.y += ovrframe.size.height;
5384 trnframe.size.height -= ovrframe.size.height;
5385 [transition_ setFrame:trnframe];
5386
5387 [UIView endAnimations];
5388
5389 [indicator_ startAnimation];
5390 [prompt_ setText:@"Updating Database"];
5391 [progress_ setProgress:0];
5392
5393 received_ = 0;
5394 last_ = [NSDate timeIntervalSinceReferenceDate];
5395 updating_ = true;
5396 [overlay_ addSubview:cancel_];
5397
5398 [NSThread
5399 detachNewThreadSelector:@selector(_update)
5400 toTarget:self
5401 withObject:nil
5402 ];
5403 }
5404
5405 - (void) _update_ {
5406 updating_ = false;
5407
5408 [indicator_ stopAnimation];
5409
5410 [UIView beginAnimations:nil context:NULL];
5411
5412 CGRect ovrframe = [overlay_ frame];
5413 ovrframe.origin.y = -ovrframe.size.height;
5414 [overlay_ setFrame:ovrframe];
5415
5416 CGRect barframe = [navbar_ frame];
5417 barframe.origin.y -= ovrframe.size.height;
5418 [navbar_ setFrame:barframe];
5419
5420 CGRect trnframe = [transition_ frame];
5421 trnframe.origin.y -= ovrframe.size.height;
5422 trnframe.size.height += ovrframe.size.height;
5423 [transition_ setFrame:trnframe];
5424
5425 [UIView commitAnimations];
5426
5427 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5428 }
5429
5430 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5431 if ((self = [super initWithFrame:frame]) != nil) {
5432 database_ = database;
5433
5434 CGRect ovrrect = [navbar_ bounds];
5435 ovrrect.size.height = [UINavigationBar defaultSize].height;
5436 ovrrect.origin.y = -ovrrect.size.height;
5437
5438 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5439 [self addSubview:overlay_];
5440
5441 [overlay_ setBarStyle:1];
5442 int barstyle = [overlay_ _barStyle:NO];
5443 bool ugly = barstyle == 0;
5444
5445 UIProgressIndicatorStyle style = ugly ?
5446 kUIProgressIndicatorStyleMediumBrown :
5447 kUIProgressIndicatorStyleMediumWhite;
5448
5449 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5450 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5451 CGRect indrect = {{indoffset, indoffset}, indsize};
5452
5453 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5454 [indicator_ setStyle:style];
5455 [overlay_ addSubview:indicator_];
5456
5457 CGSize prmsize = {215, indsize.height + 4};
5458
5459 CGRect prmrect = {{
5460 indoffset * 2 + indsize.width,
5461 #ifdef __OBJC2__
5462 -1 +
5463 #endif
5464 unsigned(ovrrect.size.height - prmsize.height) / 2
5465 }, prmsize};
5466
5467 UIFont *font = [UIFont systemFontOfSize:15];
5468
5469 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5470
5471 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5472 [prompt_ setBackgroundColor:[UIColor clearColor]];
5473 [prompt_ setFont:font];
5474
5475 [overlay_ addSubview:prompt_];
5476
5477 CGSize prgsize = {75, 100};
5478
5479 CGRect prgrect = {{
5480 ovrrect.size.width - prgsize.width - 10,
5481 (ovrrect.size.height - prgsize.height) / 2
5482 } , prgsize};
5483
5484 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5485 [progress_ setStyle:0];
5486 [overlay_ addSubview:progress_];
5487
5488 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5489 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5490
5491 CGRect frame = [cancel_ frame];
5492 frame.size.width = 65;
5493 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5494 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5495 [cancel_ setFrame:frame];
5496
5497 [cancel_ setBarStyle:barstyle];
5498 } return self;
5499 }
5500
5501 - (void) _onCancel {
5502 updating_ = false;
5503 [cancel_ removeFromSuperview];
5504 }
5505
5506 - (void) _update {
5507 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5508
5509 Status status;
5510 status.setDelegate(self);
5511
5512 [database_ updateWithStatus:status];
5513
5514 [self
5515 performSelectorOnMainThread:@selector(_update_)
5516 withObject:nil
5517 waitUntilDone:NO
5518 ];
5519
5520 [pool release];
5521 }
5522
5523 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5524 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5525 }
5526
5527 - (void) setProgressTitle:(NSString *)title {
5528 [self
5529 performSelectorOnMainThread:@selector(_setProgressTitle:)
5530 withObject:title
5531 waitUntilDone:YES
5532 ];
5533 }
5534
5535 - (void) setProgressPercent:(float)percent {
5536 [self
5537 performSelectorOnMainThread:@selector(_setProgressPercent:)
5538 withObject:[NSNumber numberWithFloat:percent]
5539 waitUntilDone:YES
5540 ];
5541 }
5542
5543 - (void) startProgress {
5544 }
5545
5546 - (void) addProgressOutput:(NSString *)output {
5547 [self
5548 performSelectorOnMainThread:@selector(_addProgressOutput:)
5549 withObject:output
5550 waitUntilDone:YES
5551 ];
5552 }
5553
5554 - (bool) isCancelling:(size_t)received {
5555 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5556 if (received_ != received) {
5557 received_ = received;
5558 last_ = now;
5559 } else if (now - last_ > 15)
5560 return true;
5561 return !updating_;
5562 }
5563
5564 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5565 [sheet dismiss];
5566 }
5567
5568 - (void) _setProgressTitle:(NSString *)title {
5569 [prompt_ setText:title];
5570 }
5571
5572 - (void) _setProgressPercent:(NSNumber *)percent {
5573 [progress_ setProgress:[percent floatValue]];
5574 }
5575
5576 - (void) _addProgressOutput:(NSString *)output {
5577 }
5578
5579 @end
5580
5581 @interface Cydia : UIApplication <
5582 ConfirmationViewDelegate,
5583 ProgressViewDelegate,
5584 SearchViewDelegate,
5585 CydiaDelegate
5586 > {
5587 UIWindow *window_;
5588
5589 UIView *underlay_;
5590 UIView *overlay_;
5591 CYBook *book_;
5592 UIToolbar *buttonbar_;
5593
5594 ConfirmationView *confirm_;
5595
5596 NSMutableArray *essential_;
5597 NSMutableArray *broken_;
5598
5599 Database *database_;
5600 ProgressView *progress_;
5601
5602 unsigned tag_;
5603
5604 UIKeyboard *keyboard_;
5605 UIProgressHUD *hud_;
5606
5607 InstallView *install_;
5608 ChangesView *changes_;
5609 ManageView *manage_;
5610 SearchView *search_;
5611 }
5612
5613 @end
5614
5615 @implementation Cydia
5616
5617 - (void) _loaded {
5618 if ([broken_ count] != 0) {
5619 int count = [broken_ count];
5620
5621 UIActionSheet *sheet = [[[UIActionSheet alloc]
5622 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5623 buttons:[NSArray arrayWithObjects:
5624 @"Forcibly Clear",
5625 @"Ignore (Temporary)",
5626 nil]
5627 defaultButtonIndex:0
5628 delegate:self
5629 context:@"fixhalf"
5630 ] autorelease];
5631
5632 [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."];
5633 [sheet popupAlertAnimated:YES];
5634 } else if (!Ignored_ && [essential_ count] != 0) {
5635 int count = [essential_ count];
5636
5637 UIActionSheet *sheet = [[[UIActionSheet alloc]
5638 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5639 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5640 defaultButtonIndex:0
5641 delegate:self
5642 context:@"upgrade"
5643 ] autorelease];
5644
5645 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5646 [sheet popupAlertAnimated:YES];
5647 }
5648 }
5649
5650 - (void) _reloadData {
5651 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5652 [hud setText:@"Reloading Data"];
5653 [overlay_ addSubview:hud];
5654 [hud show:YES];*/
5655
5656 [database_ reloadData];
5657
5658 size_t changes(0);
5659
5660 [essential_ removeAllObjects];
5661 [broken_ removeAllObjects];
5662
5663 NSArray *packages = [database_ packages];
5664 for (int i(0), e([packages count]); i != e; ++i) {
5665 Package *package = [packages objectAtIndex:i];
5666 if ([package half])
5667 [broken_ addObject:package];
5668 if ([package upgradableAndEssential:NO]) {
5669 if ([package essential])
5670 [essential_ addObject:package];
5671 ++changes;
5672 }
5673 }
5674
5675 if (changes != 0) {
5676 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5677 [buttonbar_ setBadgeValue:badge forButton:3];
5678 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5679 [buttonbar_ setBadgeAnimated:YES forButton:3];
5680 [self setApplicationBadge:badge];
5681 } else {
5682 [buttonbar_ setBadgeValue:nil forButton:3];
5683 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5684 [buttonbar_ setBadgeAnimated:NO forButton:3];
5685 [self removeApplicationBadge];
5686 }
5687
5688 [self updateData];
5689
5690 if ([packages count] == 0);
5691 else if (Loaded_)
5692 [self _loaded];
5693 else {
5694 Loaded_ = YES;
5695 [book_ update];
5696 }
5697
5698 /*[hud show:NO];
5699 [hud removeFromSuperview];*/
5700 }
5701
5702 - (void) _saveConfig {
5703 if (Changed_) {
5704 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5705 Changed_ = false;
5706 }
5707 }
5708
5709 - (void) updateData {
5710 [self _saveConfig];
5711
5712 /* XXX: this is just stupid */
5713 if (tag_ != 2)
5714 [install_ reloadData];
5715 if (tag_ != 3)
5716 [changes_ reloadData];
5717 if (tag_ != 5)
5718 [search_ reloadData];
5719
5720 [book_ reloadData];
5721 }
5722
5723 - (void) update_ {
5724 [database_ update];
5725 }
5726
5727 - (void) syncData {
5728 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5729 _assert(file != NULL);
5730
5731 NSArray *keys = [Sources_ allKeys];
5732
5733 for (int i(0), e([keys count]); i != e; ++i) {
5734 NSString *key = [keys objectAtIndex:i];
5735 NSDictionary *source = [Sources_ objectForKey:key];
5736
5737 fprintf(file, "%s %s %s\n",
5738 [[source objectForKey:@"Type"] UTF8String],
5739 [[source objectForKey:@"URI"] UTF8String],
5740 [[source objectForKey:@"Distribution"] UTF8String]
5741 );
5742 }
5743
5744 fclose(file);
5745
5746 [self _saveConfig];
5747
5748 [progress_
5749 detachNewThreadSelector:@selector(update_)
5750 toTarget:self
5751 withObject:nil
5752 title:@"Updating Sources"
5753 ];
5754 }
5755
5756 - (void) reloadData {
5757 @synchronized (self) {
5758 if (confirm_ == nil)
5759 [self _reloadData];
5760 }
5761 }
5762
5763 - (void) resolve {
5764 pkgProblemResolver *resolver = [database_ resolver];
5765
5766 resolver->InstallProtect();
5767 if (!resolver->Resolve(true))
5768 _error->Discard();
5769 }
5770
5771 - (void) perform {
5772 [database_ prepare];
5773
5774 if ([database_ cache]->BrokenCount() == 0)
5775 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5776 else {
5777 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5778 NSArray *packages = [database_ packages];
5779
5780 for (size_t i(0); i != [packages count]; ++i) {
5781 Package *package = [packages objectAtIndex:i];
5782 if ([package broken])
5783 [broken addObject:[package name]];
5784 }
5785
5786 UIActionSheet *sheet = [[[UIActionSheet alloc]
5787 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5788 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5789 defaultButtonIndex:0
5790 delegate:self
5791 context:@"broken"
5792 ] autorelease];
5793
5794 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5795 [sheet popupAlertAnimated:YES];
5796
5797 [self _reloadData];
5798 }
5799 }
5800
5801 - (void) installPackage:(Package *)package {
5802 @synchronized (self) {
5803 [package install];
5804 [self resolve];
5805 [self perform];
5806 }
5807 }
5808
5809 - (void) removePackage:(Package *)package {
5810 @synchronized (self) {
5811 [package remove];
5812 [self resolve];
5813 [self perform];
5814 }
5815 }
5816
5817 - (void) distUpgrade {
5818 @synchronized (self) {
5819 [database_ upgrade];
5820 [self perform];
5821 }
5822 }
5823
5824 - (void) cancel {
5825 @synchronized (self) {
5826 [confirm_ release];
5827 confirm_ = nil;
5828 [self _reloadData];
5829 }
5830 }
5831
5832 - (void) confirm {
5833 [overlay_ removeFromSuperview];
5834 reload_ = true;
5835
5836 [progress_
5837 detachNewThreadSelector:@selector(perform)
5838 toTarget:database_
5839 withObject:nil
5840 title:@"Running"
5841 ];
5842 }
5843
5844 - (void) bootstrap_ {
5845 [database_ update];
5846 [database_ upgrade];
5847 [database_ prepare];
5848 [database_ perform];
5849 }
5850
5851 - (void) bootstrap {
5852 [progress_
5853 detachNewThreadSelector:@selector(bootstrap_)
5854 toTarget:self
5855 withObject:nil
5856 title:@"Bootstrap Install"
5857 ];
5858 }
5859
5860 - (void) progressViewIsComplete:(ProgressView *)progress {
5861 @synchronized (self) {
5862 [self _reloadData];
5863
5864 if (confirm_ != nil) {
5865 [underlay_ addSubview:overlay_];
5866 [confirm_ removeFromSuperview];
5867 [confirm_ release];
5868 confirm_ = nil;
5869 }
5870 }
5871 }
5872
5873 - (void) setPage:(RVPage *)page {
5874 [page resetViewAnimated:NO];
5875 [page setDelegate:self];
5876 [book_ setPage:page];
5877 }
5878
5879 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5880 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5881 [browser loadURL:url];
5882 return browser;
5883 }
5884
5885 - (void) _setHomePage {
5886 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5887 }
5888
5889 - (void) buttonBarItemTapped:(id)sender {
5890 unsigned tag = [sender tag];
5891 if (tag == tag_) {
5892 [book_ resetViewAnimated:YES];
5893 return;
5894 } else if (tag_ == 2 && tag != 2)
5895 [install_ resetView];
5896
5897 switch (tag) {
5898 case 1: [self _setHomePage]; break;
5899
5900 case 2: [self setPage:install_]; break;
5901 case 3: [self setPage:changes_]; break;
5902 case 4: [self setPage:manage_]; break;
5903 case 5: [self setPage:search_]; break;
5904
5905 default: _assert(false);
5906 }
5907
5908 tag_ = tag;
5909 }
5910
5911 - (void) fixSpringBoard {
5912 pid_t pid = ExecFork();
5913 if (pid == 0) {
5914 sleep(1);
5915
5916 if (pid_t child = fork()) {
5917 waitpid(child, NULL, 0);
5918 } else {
5919 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5920 perror("launchctl unload");
5921 exit(0);
5922 }
5923
5924 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5925 perror("launchctl load");
5926 exit(0);
5927 }
5928 }
5929
5930 - (void) applicationWillSuspend {
5931 [database_ clean];
5932
5933 if (reload_) {
5934 #ifndef __OBJC2__
5935 [self fixSpringBoard];
5936 #endif
5937 }
5938
5939 [super applicationWillSuspend];
5940 }
5941
5942 - (void) askForSettings {
5943 UIActionSheet *role = [[[UIActionSheet alloc]
5944 initWithTitle:@"Who Are You?"
5945 buttons:[NSArray arrayWithObjects:
5946 @"User (Graphical Only)",
5947 @"Hacker (+ Command Line)",
5948 @"Developer (No Filters)",
5949 nil]
5950 defaultButtonIndex:-1
5951 delegate:self
5952 context:@"role"
5953 ] autorelease];
5954
5955 [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."];
5956 [role popupAlertAnimated:YES];
5957 }
5958
5959 - (void) finish {
5960 if (hud_ != nil) {
5961 [self setStatusBarShowsProgress:NO];
5962
5963 [hud_ show:NO];
5964 [hud_ removeFromSuperview];
5965 [hud_ autorelease];
5966 hud_ = nil;
5967
5968 pid_t pid = ExecFork();
5969 if (pid == 0) {
5970 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5971 perror("launchctl stop");
5972 }
5973
5974 return;
5975 }
5976
5977 if (Role_ == nil) {
5978 [self askForSettings];
5979 return;
5980 }
5981
5982 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5983
5984 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5985 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5986 0, 0, screenrect.size.width, screenrect.size.height - 48
5987 ) database:database_];
5988
5989 [book_ setDelegate:self];
5990
5991 [overlay_ addSubview:book_];
5992
5993 NSArray *buttonitems = [NSArray arrayWithObjects:
5994 [NSDictionary dictionaryWithObjectsAndKeys:
5995 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5996 @"home-up.png", kUIButtonBarButtonInfo,
5997 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5998 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5999 self, kUIButtonBarButtonTarget,
6000 @"Home", kUIButtonBarButtonTitle,
6001 @"0", kUIButtonBarButtonType,
6002 nil],
6003
6004 [NSDictionary dictionaryWithObjectsAndKeys:
6005 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6006 @"install-up.png", kUIButtonBarButtonInfo,
6007 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6008 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6009 self, kUIButtonBarButtonTarget,
6010 @"Sections", kUIButtonBarButtonTitle,
6011 @"0", kUIButtonBarButtonType,
6012 nil],
6013
6014 [NSDictionary dictionaryWithObjectsAndKeys:
6015 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6016 @"changes-up.png", kUIButtonBarButtonInfo,
6017 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6018 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6019 self, kUIButtonBarButtonTarget,
6020 @"Changes", kUIButtonBarButtonTitle,
6021 @"0", kUIButtonBarButtonType,
6022 nil],
6023
6024 [NSDictionary dictionaryWithObjectsAndKeys:
6025 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6026 @"manage-up.png", kUIButtonBarButtonInfo,
6027 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6028 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6029 self, kUIButtonBarButtonTarget,
6030 @"Manage", kUIButtonBarButtonTitle,
6031 @"0", kUIButtonBarButtonType,
6032 nil],
6033
6034 [NSDictionary dictionaryWithObjectsAndKeys:
6035 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6036 @"search-up.png", kUIButtonBarButtonInfo,
6037 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6038 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6039 self, kUIButtonBarButtonTarget,
6040 @"Search", kUIButtonBarButtonTitle,
6041 @"0", kUIButtonBarButtonType,
6042 nil],
6043 nil];
6044
6045 buttonbar_ = [[UIToolbar alloc]
6046 initInView:overlay_
6047 withFrame:CGRectMake(
6048 0, screenrect.size.height - ButtonBarHeight_,
6049 screenrect.size.width, ButtonBarHeight_
6050 )
6051 withItemList:buttonitems
6052 ];
6053
6054 [buttonbar_ setDelegate:self];
6055 [buttonbar_ setBarStyle:1];
6056 [buttonbar_ setButtonBarTrackingMode:2];
6057
6058 int buttons[5] = {1, 2, 3, 4, 5};
6059 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6060 [buttonbar_ showButtonGroup:0 withDuration:0];
6061
6062 for (int i = 0; i != 5; ++i)
6063 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6064 i * 64 + 2, 1, 60, ButtonBarHeight_
6065 )];
6066
6067 [buttonbar_ showSelectionForButton:1];
6068 [overlay_ addSubview:buttonbar_];
6069
6070 [UIKeyboard initImplementationNow];
6071 CGSize keysize = [UIKeyboard defaultSize];
6072 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6073 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6074 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6075 [overlay_ addSubview:keyboard_];
6076
6077 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6078 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6079 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6080
6081 manage_ = (ManageView *) [[self
6082 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6083 withClass:[ManageView class]
6084 ] retain];
6085
6086 if (!bootstrap_)
6087 [underlay_ addSubview:overlay_];
6088
6089 [self reloadData];
6090
6091 if (bootstrap_)
6092 [self bootstrap];
6093 else
6094 [self _setHomePage];
6095 }
6096
6097 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6098 NSString *context = [sheet context];
6099 if ([context isEqualToString:@"fixhalf"])
6100 switch (button) {
6101 case 1:
6102 @synchronized (self) {
6103 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6104 Package *broken = [broken_ objectAtIndex:i];
6105 [broken remove];
6106
6107 NSString *id = [broken id];
6108 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6109 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6110 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6111 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6112 }
6113
6114 [self resolve];
6115 [self perform];
6116 }
6117 break;
6118
6119 case 2:
6120 [broken_ removeAllObjects];
6121 [self _loaded];
6122 break;
6123
6124 default:
6125 _assert(false);
6126 }
6127 else if ([context isEqualToString:@"role"]) {
6128 switch (button) {
6129 case 1: Role_ = @"User"; break;
6130 case 2: Role_ = @"Hacker"; break;
6131 case 3: Role_ = @"Developer"; break;
6132
6133 default:
6134 Role_ = nil;
6135 _assert(false);
6136 }
6137
6138 bool reset = Settings_ != nil;
6139
6140 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6141 Role_, @"Role",
6142 nil];
6143
6144 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6145
6146 Changed_ = true;
6147
6148 if (reset)
6149 [self updateData];
6150 else
6151 [self finish];
6152 } else if ([context isEqualToString:@"upgrade"])
6153 switch (button) {
6154 case 1:
6155 @synchronized (self) {
6156 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6157 Package *essential = [essential_ objectAtIndex:i];
6158 [essential install];
6159 }
6160
6161 [self resolve];
6162 [self perform];
6163 }
6164 break;
6165
6166 case 2:
6167 Ignored_ = YES;
6168 break;
6169
6170 default:
6171 _assert(false);
6172 }
6173
6174 [sheet dismiss];
6175 }
6176
6177 - (void) reorganize {
6178 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6179 system("/usr/libexec/cydia/free.sh");
6180 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6181 [pool release];
6182 }
6183
6184 - (void) applicationSuspend:(__GSEvent *)event {
6185 if (hud_ == nil && ![progress_ isRunning])
6186 [super applicationSuspend:event];
6187 }
6188
6189 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6190 if (hud_ == nil)
6191 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6192 }
6193
6194 - (void) _setSuspended:(BOOL)value {
6195 if (hud_ == nil)
6196 [super _setSuspended:value];
6197 }
6198
6199 - (UIProgressHUD *) addProgressHUD {
6200 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6201 [hud show:YES];
6202 [underlay_ addSubview:hud];
6203 return hud;
6204 }
6205
6206 - (void) applicationDidFinishLaunching:(id)unused {
6207 Font12_ = [[UIFont systemFontOfSize:12] retain];
6208 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6209 Font14_ = [[UIFont systemFontOfSize:14] retain];
6210 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6211 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6212
6213 _assert(pkgInitConfig(*_config));
6214 _assert(pkgInitSystem(*_config, _system));
6215
6216 confirm_ = nil;
6217 tag_ = 1;
6218
6219 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6220 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6221
6222 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6223 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6224
6225 [window_ orderFront:self];
6226 [window_ makeKey:self];
6227 [window_ _setHidden:NO];
6228
6229 database_ = [[Database alloc] init];
6230 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6231 [database_ setDelegate:progress_];
6232 [window_ setContentView:progress_];
6233
6234 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6235 [progress_ setContentView:underlay_];
6236
6237 [progress_ resetView];
6238
6239 if (
6240 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6241 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6242 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6243 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6244 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6245 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6246 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6247 ) {
6248 [self setIdleTimerDisabled:YES];
6249
6250 hud_ = [self addProgressHUD];
6251 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6252
6253 [self setStatusBarShowsProgress:YES];
6254
6255 [NSThread
6256 detachNewThreadSelector:@selector(reorganize)
6257 toTarget:self
6258 withObject:nil
6259 ];
6260 } else
6261 [self finish];
6262 }
6263
6264 - (void) showKeyboard:(BOOL)show {
6265 CGSize keysize = [UIKeyboard defaultSize];
6266 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6267 CGRect keyup = keydown;
6268 keyup.origin.y -= keysize.height;
6269
6270 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6271 [animation setSignificantRectFields:2];
6272
6273 if (show) {
6274 [animation setStartFrame:keydown];
6275 [animation setEndFrame:keyup];
6276 [keyboard_ activate];
6277 } else {
6278 [animation setStartFrame:keyup];
6279 [animation setEndFrame:keydown];
6280 [keyboard_ deactivate];
6281 }
6282
6283 [[UIAnimator sharedAnimator]
6284 addAnimations:[NSArray arrayWithObjects:animation, nil]
6285 withDuration:KeyboardTime_
6286 start:YES
6287 ];
6288 }
6289
6290 - (void) slideUp:(UIActionSheet *)alert {
6291 if (Advanced_)
6292 [alert presentSheetFromButtonBar:buttonbar_];
6293 else
6294 [alert presentSheetInView:overlay_];
6295 }
6296
6297 @end
6298
6299 void AddPreferences(NSString *plist) {
6300 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6301
6302 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6303 _assert(settings != NULL);
6304 NSMutableArray *items = [settings objectForKey:@"items"];
6305
6306 bool cydia(false);
6307
6308 for (size_t i(0); i != [items count]; ++i) {
6309 NSMutableDictionary *item([items objectAtIndex:i]);
6310 NSString *label = [item objectForKey:@"label"];
6311 if (label != nil && [label isEqualToString:@"Cydia"]) {
6312 cydia = true;
6313 break;
6314 }
6315 }
6316
6317 if (!cydia) {
6318 for (size_t i(0); i != [items count]; ++i) {
6319 NSDictionary *item([items objectAtIndex:i]);
6320 NSString *label = [item objectForKey:@"label"];
6321 if (label != nil && [label isEqualToString:@"General"]) {
6322 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6323 @"CydiaSettings", @"bundle",
6324 @"PSLinkCell", @"cell",
6325 [NSNumber numberWithBool:YES], @"hasIcon",
6326 [NSNumber numberWithBool:YES], @"isController",
6327 @"Cydia", @"label",
6328 nil] atIndex:(i + 1)];
6329
6330 break;
6331 }
6332 }
6333
6334 _assert([settings writeToFile:plist atomically:YES] == YES);
6335 }
6336
6337 [pool release];
6338 }
6339
6340 /*IMP alloc_;
6341 id Alloc_(id self, SEL selector) {
6342 id object = alloc_(self, selector);
6343 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6344 return object;
6345 }*/
6346
6347 /*IMP dealloc_;
6348 id Dealloc_(id self, SEL selector) {
6349 id object = dealloc_(self, selector);
6350 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6351 return object;
6352 }*/
6353
6354 int main(int argc, char *argv[]) {
6355 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6356
6357 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6358
6359 App_ = [[NSBundle mainBundle] bundlePath];
6360 Home_ = NSHomeDirectory();
6361
6362 {
6363 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6364 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6365 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6366 Sounds_Keyboard_ = [keyboard boolValue];
6367 }
6368
6369 setuid(0);
6370 setgid(0);
6371
6372 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6373 _assert(errno == ENOENT);
6374 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6375 _assert(errno == ENOENT);
6376
6377 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6378 alloc_ = alloc->method_imp;
6379 alloc->method_imp = (IMP) &Alloc_;*/
6380
6381 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6382 dealloc_ = dealloc->method_imp;
6383 dealloc->method_imp = (IMP) &Dealloc_;*/
6384
6385 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6386 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6387 Firmware_ = strdup([prover UTF8String]);
6388 NSArray *versions = [prover componentsSeparatedByString:@"."];
6389 int count = [versions count];
6390 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6391 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6392 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6393 }
6394 }
6395
6396 size_t size;
6397 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6398 char *machine = new char[size];
6399 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6400 Machine_ = machine;
6401
6402 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6403 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6404 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6405 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6406 CFRelease(serial);
6407 }
6408
6409 IOObjectRelease(service);
6410 }
6411
6412 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6413 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6414
6415 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6416 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6417 else {
6418 Settings_ = [Metadata_ objectForKey:@"Settings"];
6419
6420 Packages_ = [Metadata_ objectForKey:@"Packages"];
6421 Sections_ = [Metadata_ objectForKey:@"Sections"];
6422 Sources_ = [Metadata_ objectForKey:@"Sources"];
6423 }
6424
6425 if (Settings_ != nil)
6426 Role_ = [Settings_ objectForKey:@"Role"];
6427
6428 if (Packages_ == nil) {
6429 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6430 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6431 }
6432
6433 if (Sections_ == nil) {
6434 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6435 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6436 }
6437
6438 if (Sources_ == nil) {
6439 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6440 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6441 }
6442
6443 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
6444
6445 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6446 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6447
6448 if (access("/User", F_OK) != 0)
6449 system("/usr/libexec/cydia/firmware.sh");
6450
6451 Locale_ = CFLocaleCopyCurrent();
6452 space_ = CGColorSpaceCreateDeviceRGB();
6453
6454 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6455 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6456 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6457 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
6458 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6459 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6460 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6461
6462 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6463
6464 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6465
6466 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
6467
6468 CGColorSpaceRelease(space_);
6469 CFRelease(Locale_);
6470
6471 [pool release];
6472 return value;
6473 }