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