]> git.saurik.com Git - cydia.git/blob - Cydia.mm
OMG, I forgot a file.
[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 BOOL running_;
2161 }
2162
2163 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2164
2165 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2166 - (void) setContentView:(UIView *)view;
2167 - (void) resetView;
2168
2169 - (void) _retachThread;
2170 - (void) _detachNewThreadData:(ProgressData *)data;
2171 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2172
2173 - (BOOL) isRunning;
2174
2175 @end
2176
2177 @protocol ProgressViewDelegate
2178 - (void) progressViewIsComplete:(ProgressView *)sender;
2179 @end
2180
2181 @implementation ProgressView
2182
2183 - (void) dealloc {
2184 [transition_ setDelegate:nil];
2185 [navbar_ setDelegate:nil];
2186
2187 [view_ release];
2188 if (background_ != nil)
2189 [background_ release];
2190 [transition_ release];
2191 [overlay_ release];
2192 [navbar_ release];
2193 [progress_ release];
2194 [output_ release];
2195 [status_ release];
2196 [close_ release];
2197 [super dealloc];
2198 }
2199
2200 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2201 if (bootstrap_ && from == overlay_ && to == view_)
2202 exit(0);
2203 }
2204
2205 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2206 if ((self = [super initWithFrame:frame]) != nil) {
2207 database_ = database;
2208 delegate_ = delegate;
2209
2210 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2211 [transition_ setDelegate:self];
2212
2213 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2214
2215 if (bootstrap_)
2216 [overlay_ setBackgroundColor:Black_];
2217 else {
2218 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2219 [background_ setBackgroundColor:Black_];
2220 [self addSubview:background_];
2221 }
2222
2223 [self addSubview:transition_];
2224
2225 CGSize navsize = [UINavigationBar defaultSize];
2226 CGRect navrect = {{0, 0}, navsize};
2227
2228 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2229 [overlay_ addSubview:navbar_];
2230
2231 [navbar_ setBarStyle:1];
2232 [navbar_ setDelegate:self];
2233
2234 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2235 [navbar_ pushNavigationItem:navitem];
2236
2237 CGRect bounds = [overlay_ bounds];
2238 CGSize prgsize = [UIProgressBar defaultSize];
2239
2240 CGRect prgrect = {{
2241 (bounds.size.width - prgsize.width) / 2,
2242 bounds.size.height - prgsize.height - 20
2243 }, prgsize};
2244
2245 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2246 [progress_ setStyle:0];
2247
2248 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2249 10,
2250 bounds.size.height - prgsize.height - 50,
2251 bounds.size.width - 20,
2252 24
2253 )];
2254
2255 [status_ setColor:White_];
2256 [status_ setBackgroundColor:Clear_];
2257
2258 [status_ setCentersHorizontally:YES];
2259 //[status_ setFont:font];
2260
2261 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2262 10,
2263 navrect.size.height + 20,
2264 bounds.size.width - 20,
2265 bounds.size.height - navsize.height - 62 - navrect.size.height
2266 )];
2267
2268 //[output_ setTextFont:@"Courier New"];
2269 [output_ setTextSize:12];
2270
2271 [output_ setTextColor:White_];
2272 [output_ setBackgroundColor:Clear_];
2273
2274 [output_ setMarginTop:0];
2275 [output_ setAllowsRubberBanding:YES];
2276 [output_ setEditable:NO];
2277
2278 [overlay_ addSubview:output_];
2279
2280 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2281 10,
2282 bounds.size.height - prgsize.height - 50,
2283 bounds.size.width - 20,
2284 32 + prgsize.height
2285 )];
2286
2287 [close_ setAutosizesToFit:NO];
2288 [close_ setDrawsShadow:YES];
2289 [close_ setStretchBackground:YES];
2290 [close_ setTitle:@"Close Window"];
2291 [close_ setEnabled:YES];
2292
2293 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2294 [close_ setTitleFont:bold];
2295 CFRelease(bold);
2296
2297 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2298 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2299 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2300 } return self;
2301 }
2302
2303 - (void) setContentView:(UIView *)view {
2304 view_ = [view retain];
2305 }
2306
2307 - (void) resetView {
2308 [transition_ transition:6 toView:view_];
2309 }
2310
2311 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2312 NSString *context = [sheet context];
2313 if ([context isEqualToString:@"conffile"]) {
2314 FILE *input = [database_ input];
2315
2316 switch (button) {
2317 case 1:
2318 fprintf(input, "N\n");
2319 fflush(input);
2320 break;
2321 case 2:
2322 fprintf(input, "Y\n");
2323 fflush(input);
2324 break;
2325 default:
2326 _assert(false);
2327 }
2328 }
2329
2330 [sheet dismiss];
2331 }
2332
2333 - (void) closeButtonPushed {
2334 [delegate_ progressViewIsComplete:self];
2335 [self resetView];
2336 }
2337
2338 - (void) _retachThread {
2339 UINavigationItem *item = [navbar_ topItem];
2340 [item setTitle:@"Complete"];
2341
2342 [overlay_ addSubview:close_];
2343 [progress_ removeFromSuperview];
2344 [status_ removeFromSuperview];
2345
2346 [delegate_ setStatusBarShowsProgress:NO];
2347 running_ = NO;
2348 }
2349
2350 - (void) _detachNewThreadData:(ProgressData *)data {
2351 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2352
2353 [[data target] performSelector:[data selector] withObject:[data object]];
2354 [data release];
2355
2356 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2357
2358 [pool release];
2359 }
2360
2361 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2362 UINavigationItem *item = [navbar_ topItem];
2363 [item setTitle:title];
2364
2365 [status_ setText:nil];
2366 [output_ setText:@""];
2367 [progress_ setProgress:0];
2368
2369 [close_ removeFromSuperview];
2370 [overlay_ addSubview:progress_];
2371 [overlay_ addSubview:status_];
2372
2373 [delegate_ setStatusBarShowsProgress:YES];
2374 running_ = YES;
2375
2376 [transition_ transition:6 toView:overlay_];
2377
2378 [NSThread
2379 detachNewThreadSelector:@selector(_detachNewThreadData:)
2380 toTarget:self
2381 withObject:[[ProgressData alloc]
2382 initWithSelector:selector
2383 target:target
2384 object:object
2385 ]
2386 ];
2387 }
2388
2389 - (void) repairWithSelector:(SEL)selector {
2390 [self
2391 detachNewThreadSelector:selector
2392 toTarget:database_
2393 withObject:nil
2394 title:@"Repairing..."
2395 ];
2396 }
2397
2398 - (void) setConfigurationData:(NSString *)data {
2399 [self
2400 performSelectorOnMainThread:@selector(_setConfigurationData:)
2401 withObject:data
2402 waitUntilDone:YES
2403 ];
2404 }
2405
2406 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2407 Package *package = id == nil ? nil : [database_ packageWithName:id];
2408
2409 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2410 initWithTitle:(package == nil ? @"Source Error" : [package name])
2411 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2412 defaultButtonIndex:0
2413 delegate:self
2414 context:@"error"
2415 ] autorelease];
2416
2417 [sheet setBodyText:error];
2418 [sheet popupAlertAnimated:YES];
2419 }
2420
2421 - (void) setProgressTitle:(NSString *)title {
2422 [self
2423 performSelectorOnMainThread:@selector(_setProgressTitle:)
2424 withObject:title
2425 waitUntilDone:YES
2426 ];
2427 }
2428
2429 - (void) setProgressPercent:(float)percent {
2430 [self
2431 performSelectorOnMainThread:@selector(_setProgressPercent:)
2432 withObject:[NSNumber numberWithFloat:percent]
2433 waitUntilDone:YES
2434 ];
2435 }
2436
2437 - (void) addProgressOutput:(NSString *)output {
2438 [self
2439 performSelectorOnMainThread:@selector(_addProgressOutput:)
2440 withObject:output
2441 waitUntilDone:YES
2442 ];
2443 }
2444
2445 - (void) _setConfigurationData:(NSString *)data {
2446 _assert(conffile_r(data));
2447
2448 NSString *ofile = conffile_r[1];
2449 //NSString *nfile = conffile_r[2];
2450
2451 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2452 initWithTitle:@"Configuration Upgrade"
2453 buttons:[NSArray arrayWithObjects:
2454 @"Keep My Old Copy",
2455 @"Accept The New Copy",
2456 // XXX: @"See What Changed",
2457 nil]
2458 defaultButtonIndex:0
2459 delegate:self
2460 context:@"conffile"
2461 ] autorelease];
2462
2463 [sheet setBodyText:[NSString stringWithFormat:
2464 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2465 , ofile]];
2466
2467 [sheet popupAlertAnimated:YES];
2468 }
2469
2470 - (void) _setProgressTitle:(NSString *)title {
2471 [status_ setText:[title stringByAppendingString:@"..."]];
2472 }
2473
2474 - (void) _setProgressPercent:(NSNumber *)percent {
2475 [progress_ setProgress:[percent floatValue]];
2476 }
2477
2478 - (void) _addProgressOutput:(NSString *)output {
2479 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2480 CGSize size = [output_ contentSize];
2481 CGRect rect = {{0, size.height}, {size.width, 0}};
2482 [output_ scrollRectToVisible:rect animated:YES];
2483 }
2484
2485 - (BOOL) isRunning {
2486 return NO;
2487 }
2488
2489 @end
2490 /* }}} */
2491
2492 /* Package Cell {{{ */
2493 @interface PackageCell : UITableCell {
2494 UIImageView *icon_;
2495 UITextLabel *name_;
2496 UITextLabel *description_;
2497 UITextLabel *source_;
2498 //UIImageView *trusted_;
2499 #ifdef USE_BADGES
2500 UIImageView *badge_;
2501 UITextLabel *status_;
2502 #endif
2503 BOOL setup_;
2504 }
2505
2506 - (PackageCell *) init;
2507 - (void) setPackage:(Package *)package;
2508
2509 - (void) _setSelected:(float)fraction;
2510 - (void) setSelected:(BOOL)selected;
2511 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2512 - (void) _setSelectionFadeFraction:(float)fraction;
2513
2514 + (int) heightForPackage:(Package *)package;
2515
2516 @end
2517
2518 @implementation PackageCell
2519
2520 - (void) dealloc {
2521 [icon_ release];
2522 [name_ release];
2523 [description_ release];
2524 [source_ release];
2525 #ifdef USE_BADGES
2526 [badge_ release];
2527 [status_ release];
2528 #endif
2529 //[trusted_ release];
2530 [super dealloc];
2531 }
2532
2533 - (PackageCell *) init {
2534 if ((self = [super init]) != nil) {
2535 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2536 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2537 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2538
2539 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2540
2541 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2542 [name_ setBackgroundColor:Clear_];
2543 [name_ setFont:bold];
2544
2545 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2546 [source_ setBackgroundColor:Clear_];
2547 [source_ setFont:large];
2548
2549 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2550 [description_ setBackgroundColor:Clear_];
2551 [description_ setFont:small];
2552
2553 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2554 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2555
2556 #ifdef USE_BADGES
2557 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2558
2559 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2560 [status_ setBackgroundColor:Clear_];
2561 [status_ setFont:small];
2562 #endif
2563
2564 /*[icon_ setImage:[UIImage applicationImageNamed:@"unknown.png"]];
2565 [icon_ zoomToScale:0.5];
2566 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];*/
2567
2568 [self addSubview:icon_];
2569 [self addSubview:name_];
2570 [self addSubview:description_];
2571 [self addSubview:source_];
2572
2573 CFRelease(small);
2574 CFRelease(large);
2575 CFRelease(bold);
2576 } return self;
2577 }
2578
2579 - (void) setPackage:(Package *)package {
2580 /*if (setup_)
2581 return;
2582 else
2583 setup_ = YES;*/
2584
2585 Source *source = [package source];
2586
2587 UIImage *image = nil;
2588 if (NSString *icon = [package icon])
2589 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2590 if (image == nil) if (NSString *icon = [source defaultIcon])
2591 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2592 if (image == nil)
2593 image = [UIImage applicationImageNamed:@"unknown.png"];
2594 [icon_ setImage:image];
2595
2596 /*if (image != nil) {
2597 CGSize size = [image size];
2598 float scale = 30 / std::max(size.width, size.height);
2599 [icon_ zoomToScale:scale];
2600 }*/
2601
2602 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2603
2604 [name_ setText:[package name]];
2605 [description_ setText:[package tagline]];
2606
2607 NSString *label = nil;
2608 bool trusted = false;
2609
2610 if (source != nil) {
2611 label = [source label];
2612 trusted = [source trusted];
2613 } else if ([[package id] isEqualToString:@"firmware"])
2614 label = @"Apple";
2615
2616 if (label == nil)
2617 label = @"Unknown/Local";
2618
2619 NSString *from = [NSString stringWithFormat:@"from %@", label];
2620
2621 NSString *section = Simplify([package section]);
2622 if (section != nil && ![section isEqualToString:label])
2623 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2624
2625 [source_ setText:from];
2626
2627 #ifdef USE_BADGES
2628 [badge_ removeFromSuperview];
2629 [status_ removeFromSuperview];
2630
2631 if (NSString *mode = [package mode]) {
2632 [badge_ setImage:[UIImage applicationImageNamed:
2633 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2634 ]];
2635
2636 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2637 [status_ setColor:Blueish_];
2638 } else if ([package half]) {
2639 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2640 [status_ setText:@"Package Damaged"];
2641 [status_ setColor:Red_];
2642 } else {
2643 [badge_ setImage:nil];
2644 [status_ setText:nil];
2645 goto done;
2646 }
2647
2648 [self addSubview:badge_];
2649 [self addSubview:status_];
2650 done:;
2651 #endif
2652 }
2653
2654 - (void) _setSelected:(float)fraction {
2655 CGColor black(space_,
2656 Interpolate(0.0, 1.0, fraction),
2657 Interpolate(0.0, 1.0, fraction),
2658 Interpolate(0.0, 1.0, fraction),
2659 1.0);
2660
2661 CGColor gray(space_,
2662 Interpolate(0.4, 1.0, fraction),
2663 Interpolate(0.4, 1.0, fraction),
2664 Interpolate(0.4, 1.0, fraction),
2665 1.0);
2666
2667 [name_ setColor:black];
2668 [description_ setColor:gray];
2669 [source_ setColor:black];
2670 }
2671
2672 - (void) setSelected:(BOOL)selected {
2673 [self _setSelected:(selected ? 1.0 : 0.0)];
2674 [super setSelected:selected];
2675 }
2676
2677 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2678 if (!fade)
2679 [self _setSelected:(selected ? 1.0 : 0.0)];
2680 [super setSelected:selected withFade:fade];
2681 }
2682
2683 - (void) _setSelectionFadeFraction:(float)fraction {
2684 [self _setSelected:fraction];
2685 [super _setSelectionFadeFraction:fraction];
2686 }
2687
2688 + (int) heightForPackage:(Package *)package {
2689 #ifdef USE_BADGES
2690 if ([package hasMode] || [package half])
2691 return 96;
2692 else
2693 #endif
2694 return 73;
2695 }
2696
2697 @end
2698 /* }}} */
2699 /* Section Cell {{{ */
2700 @interface SectionCell : UITableCell {
2701 UITextLabel *name_;
2702 UITextLabel *count_;
2703 }
2704
2705 - (id) init;
2706 - (void) setSection:(Section *)section;
2707
2708 - (void) _setSelected:(float)fraction;
2709 - (void) setSelected:(BOOL)selected;
2710 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2711 - (void) _setSelectionFadeFraction:(float)fraction;
2712
2713 @end
2714
2715 @implementation SectionCell
2716
2717 - (void) dealloc {
2718 [name_ release];
2719 [count_ release];
2720 [super dealloc];
2721 }
2722
2723 - (id) init {
2724 if ((self = [super init]) != nil) {
2725 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2726 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2727
2728 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2729 [name_ setBackgroundColor:Clear_];
2730 [name_ setFont:bold];
2731
2732 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2733 [count_ setCentersHorizontally:YES];
2734 [count_ setBackgroundColor:Clear_];
2735 [count_ setFont:small];
2736 [count_ setColor:White_];
2737
2738 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2739 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2740
2741 [self addSubview:folder];
2742 [self addSubview:name_];
2743 [self addSubview:count_];
2744
2745 [self _setSelected:0];
2746
2747 CFRelease(small);
2748 CFRelease(bold);
2749 } return self;
2750 }
2751
2752 - (void) setSection:(Section *)section {
2753 if (section == nil) {
2754 [name_ setText:@"All Packages"];
2755 [count_ setText:nil];
2756 } else {
2757 NSString *name = [section name];
2758 [name_ setText:(name == nil ? @"(No Section)" : name)];
2759 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2760 }
2761 }
2762
2763 - (void) _setSelected:(float)fraction {
2764 CGColor black(space_,
2765 Interpolate(0.0, 1.0, fraction),
2766 Interpolate(0.0, 1.0, fraction),
2767 Interpolate(0.0, 1.0, fraction),
2768 1.0);
2769
2770 [name_ setColor:black];
2771 }
2772
2773 - (void) setSelected:(BOOL)selected {
2774 [self _setSelected:(selected ? 1.0 : 0.0)];
2775 [super setSelected:selected];
2776 }
2777
2778 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2779 if (!fade)
2780 [self _setSelected:(selected ? 1.0 : 0.0)];
2781 [super setSelected:selected withFade:fade];
2782 }
2783
2784 - (void) _setSelectionFadeFraction:(float)fraction {
2785 [self _setSelected:fraction];
2786 [super _setSelectionFadeFraction:fraction];
2787 }
2788
2789 @end
2790 /* }}} */
2791
2792 /* File Table {{{ */
2793 @interface FileTable : RVPage {
2794 _transient Database *database_;
2795 Package *package_;
2796 NSString *name_;
2797 NSMutableArray *files_;
2798 UITable *list_;
2799 }
2800
2801 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2802 - (void) setPackage:(Package *)package;
2803
2804 @end
2805
2806 @implementation FileTable
2807
2808 - (void) dealloc {
2809 if (package_ != nil)
2810 [package_ release];
2811 if (name_ != nil)
2812 [name_ release];
2813 [files_ release];
2814 [list_ release];
2815 [super dealloc];
2816 }
2817
2818 - (int) numberOfRowsInTable:(UITable *)table {
2819 return files_ == nil ? 0 : [files_ count];
2820 }
2821
2822 - (float) table:(UITable *)table heightForRow:(int)row {
2823 return 24;
2824 }
2825
2826 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2827 if (reusing == nil) {
2828 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2829 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2830 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2831 CFRelease(font);
2832 }
2833 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2834 return reusing;
2835 }
2836
2837 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
2838 return NO;
2839 }
2840
2841 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2842 if ((self = [super initWithBook:book]) != nil) {
2843 database_ = database;
2844
2845 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
2846
2847 list_ = [[UITable alloc] initWithFrame:[self bounds]];
2848 [self addSubview:list_];
2849
2850 UITableColumn *column = [[[UITableColumn alloc]
2851 initWithTitle:@"Name"
2852 identifier:@"name"
2853 width:[self frame].size.width
2854 ] autorelease];
2855
2856 [list_ setDataSource:self];
2857 [list_ setSeparatorStyle:1];
2858 [list_ addTableColumn:column];
2859 [list_ setDelegate:self];
2860 [list_ setReusesTableCells:YES];
2861 } return self;
2862 }
2863
2864 - (void) setPackage:(Package *)package {
2865 if (package_ != nil) {
2866 [package_ autorelease];
2867 package_ = nil;
2868 }
2869
2870 if (name_ != nil) {
2871 [name_ release];
2872 name_ = nil;
2873 }
2874
2875 [files_ removeAllObjects];
2876
2877 if (package != nil) {
2878 package_ = [package retain];
2879 name_ = [[package id] retain];
2880
2881 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
2882
2883 {
2884 std::ifstream fin([path UTF8String]);
2885 std::string line;
2886 while (std::getline(fin, line))
2887 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
2888 }
2889
2890 if ([files_ count] != 0) {
2891 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
2892 [files_ removeObjectAtIndex:0];
2893 [files_ sortUsingSelector:@selector(compareByPath:)];
2894
2895 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
2896 [stack addObject:@"/"];
2897
2898 for (int i(0), e([files_ count]); i != e; ++i) {
2899 NSString *file = [files_ objectAtIndex:i];
2900 while (![file hasPrefix:[stack lastObject]])
2901 [stack removeLastObject];
2902 NSString *directory = [stack lastObject];
2903 [stack addObject:[file stringByAppendingString:@"/"]];
2904 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
2905 ([stack count] - 2) * 3, "",
2906 [file substringFromIndex:[directory length]]
2907 ]];
2908 }
2909 }
2910 }
2911
2912 [list_ reloadData];
2913 }
2914
2915 - (void) resetViewAnimated:(BOOL)animated {
2916 [list_ resetViewAnimated:animated];
2917 }
2918
2919 - (void) reloadData {
2920 [self setPackage:[database_ packageWithName:name_]];
2921 [self reloadButtons];
2922 }
2923
2924 - (NSString *) title {
2925 return @"Installed Files";
2926 }
2927
2928 - (NSString *) backButtonTitle {
2929 return @"Files";
2930 }
2931
2932 @end
2933 /* }}} */
2934 /* Package View {{{ */
2935 @protocol PackageViewDelegate
2936 - (void) performPackage:(Package *)package;
2937 @end
2938
2939 @interface PackageView : RVPage {
2940 _transient Database *database_;
2941 UIPreferencesTable *table_;
2942 Package *package_;
2943 NSString *name_;
2944 UITextView *description_;
2945 NSMutableArray *buttons_;
2946 }
2947
2948 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2949 - (void) setPackage:(Package *)package;
2950
2951 @end
2952
2953 @implementation PackageView
2954
2955 - (void) dealloc {
2956 [table_ setDataSource:nil];
2957 [table_ setDelegate:nil];
2958
2959 if (package_ != nil)
2960 [package_ release];
2961 if (name_ != nil)
2962 [name_ release];
2963 if (description_ != nil)
2964 [description_ release];
2965 [table_ release];
2966 [buttons_ release];
2967 [super dealloc];
2968 }
2969
2970 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2971 int number = 2;
2972 if ([package_ installed] != nil)
2973 ++number;
2974 if ([package_ source] != nil)
2975 ++number;
2976 return number;
2977 }
2978
2979 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2980 if (group-- == 0)
2981 return nil;
2982 else if ([package_ installed] != nil && group-- == 0)
2983 return @"Installed Package";
2984 else if (group-- == 0)
2985 return @"Package Details";
2986 else if ([package_ source] != nil && group-- == 0)
2987 return @"Source Information";
2988 else _assert(false);
2989 }
2990
2991 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2992 if (description_ == nil || group != 0 || row != 1)
2993 return proposed;
2994 else
2995 return [description_ visibleTextRect].size.height + TextViewOffset_;
2996 }
2997
2998 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2999 if (group-- == 0) {
3000 int number = 1;
3001 if ([package_ author] != nil)
3002 ++number;
3003 if (description_ != nil)
3004 ++number;
3005 if ([package_ website] != nil)
3006 ++number;
3007 return number;
3008 } else if ([package_ installed] != nil && group-- == 0)
3009 return 2;
3010 else if (group-- == 0) {
3011 int number = 2;
3012 if ([package_ size] != 0)
3013 ++number;
3014 if ([package_ maintainer] != nil)
3015 ++number;
3016 if ([package_ sponsor] != nil)
3017 ++number;
3018 if ([package_ relationships] != nil)
3019 ++number;
3020 if ([[package_ source] trusted])
3021 ++number;
3022 return number;
3023 } else if ([package_ source] != nil && group-- == 0) {
3024 Source *source = [package_ source];
3025 NSString *description = [source description];
3026 int number = 1;
3027 if (description != nil && ![description isEqualToString:[source label]])
3028 ++number;
3029 if ([source origin] != nil)
3030 ++number;
3031 return number;
3032 } else _assert(false);
3033 }
3034
3035 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3036 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3037 [cell setShowSelection:NO];
3038
3039 if (group-- == 0) {
3040 if (false) {
3041 } else if (row-- == 0) {
3042 [cell setTitle:[package_ name]];
3043 [cell setValue:[package_ latest]];
3044 } else if ([package_ author] != nil && row-- == 0) {
3045 [cell setTitle:@"Author"];
3046 [cell setValue:[[package_ author] name]];
3047 [cell setShowDisclosure:YES];
3048 [cell setShowSelection:YES];
3049 } else if (description_ != nil && row-- == 0) {
3050 [cell addSubview:description_];
3051 } else if ([package_ website] != nil && row-- == 0) {
3052 [cell setTitle:@"More Information"];
3053 [cell setShowDisclosure:YES];
3054 [cell setShowSelection:YES];
3055 } else _assert(false);
3056 } else if ([package_ installed] != nil && group-- == 0) {
3057 if (false) {
3058 } else if (row-- == 0) {
3059 [cell setTitle:@"Version"];
3060 NSString *installed([package_ installed]);
3061 [cell setValue:(installed == nil ? @"n/a" : installed)];
3062 } else if (row-- == 0) {
3063 [cell setTitle:@"Filesystem Content"];
3064 [cell setShowDisclosure:YES];
3065 [cell setShowSelection:YES];
3066 } else _assert(false);
3067 } else if (group-- == 0) {
3068 if (false) {
3069 } else if (row-- == 0) {
3070 [cell setTitle:@"Identifier"];
3071 [cell setValue:[package_ id]];
3072 } else if (row-- == 0) {
3073 [cell setTitle:@"Section"];
3074 NSString *section([package_ section]);
3075 [cell setValue:(section == nil ? @"n/a" : section)];
3076 } else if ([package_ size] != 0 && row-- == 0) {
3077 [cell setTitle:@"Expanded Size"];
3078 [cell setValue:SizeString([package_ size])];
3079 } else if ([package_ maintainer] != nil && row-- == 0) {
3080 [cell setTitle:@"Maintainer"];
3081 [cell setValue:[[package_ maintainer] name]];
3082 [cell setShowDisclosure:YES];
3083 [cell setShowSelection:YES];
3084 } else if ([package_ sponsor] != nil && row-- == 0) {
3085 [cell setTitle:@"Sponsor"];
3086 [cell setValue:[[package_ sponsor] name]];
3087 [cell setShowDisclosure:YES];
3088 [cell setShowSelection:YES];
3089 } else if ([package_ relationships] != nil && row-- == 0) {
3090 [cell setTitle:@"Package Relationships"];
3091 [cell setShowDisclosure:YES];
3092 [cell setShowSelection:YES];
3093 } else if ([[package_ source] trusted] && row-- == 0) {
3094 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3095 [cell setValue:@"This package has been signed."];
3096 } else _assert(false);
3097 } else if ([package_ source] != nil && group-- == 0) {
3098 Source *source = [package_ source];
3099 NSString *description = [source description];
3100
3101 if (false) {
3102 } else if (row-- == 0) {
3103 NSString *label = [source label];
3104 if (label == nil)
3105 label = [source uri];
3106 [cell setTitle:label];
3107 [cell setValue:[source version]];
3108 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3109 [cell setValue:description];
3110 } else if ([source origin] != nil && row-- == 0) {
3111 [cell setTitle:@"Origin"];
3112 [cell setValue:[source origin]];
3113 } else _assert(false);
3114 } else _assert(false);
3115
3116 return cell;
3117 }
3118
3119 - (BOOL) canSelectRow:(int)row {
3120 return YES;
3121 }
3122
3123 - (void) tableRowSelected:(NSNotification *)notification {
3124 int row = [table_ selectedRow];
3125 if (row == INT_MAX)
3126 return;
3127
3128 #define _else else goto _label; return; } _label:
3129
3130 if (true) {
3131 if (row-- == 0) {
3132 } else if (row-- == 0) {
3133 } else if ([package_ author] != nil && row-- == 0) {
3134 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3135 [[package_ author] email],
3136 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3137 [package_ name]
3138 ] stringByAddingPercentEscapes]
3139 ]]];
3140 } else if (description_ != nil && row-- == 0) {
3141 } else if ([package_ website] != nil && row-- == 0) {
3142 NSURL *url = [NSURL URLWithString:[package_ website]];
3143 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3144 [browser setDelegate:delegate_];
3145 [book_ pushPage:browser];
3146 [browser loadURL:url];
3147 } _else if ([package_ installed] != nil) {
3148 if (row-- == 0) {
3149 } else if (row-- == 0) {
3150 } else if (row-- == 0) {
3151 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3152 [files setDelegate:delegate_];
3153 [files setPackage:package_];
3154 [book_ pushPage:files];
3155 } _else if (true) {
3156 if (row-- == 0) {
3157 } else if (row-- == 0) {
3158 } else if (row-- == 0) {
3159 } else if ([package_ size] != 0 && row-- == 0) {
3160 } else if ([package_ maintainer] != nil && row-- == 0) {
3161 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3162 [[package_ maintainer] email],
3163 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3164 [package_ name]
3165 ] stringByAddingPercentEscapes]
3166 ]]];
3167 } else if ([package_ sponsor] != nil && row-- == 0) {
3168 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3169 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3170 [browser setDelegate:delegate_];
3171 [book_ pushPage:browser];
3172 [browser loadURL:url];
3173 } else if ([package_ relationships] != nil && row-- == 0) {
3174 } else if ([[package_ source] trusted] && row-- == 0) {
3175 } _else if ([package_ source] != nil) {
3176 Source *source = [package_ source];
3177 NSString *description = [source description];
3178
3179 if (row-- == 0) {
3180 } else if (row-- == 0) {
3181 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3182 } else if ([source origin] != nil && row-- == 0) {
3183 } _else _assert(false);
3184
3185 #undef _else
3186 }
3187
3188 - (void) _clickButtonWithName:(NSString *)name {
3189 if ([name isEqualToString:@"Install"])
3190 [delegate_ installPackage:package_];
3191 else if ([name isEqualToString:@"Reinstall"])
3192 [delegate_ installPackage:package_];
3193 else if ([name isEqualToString:@"Remove"])
3194 [delegate_ removePackage:package_];
3195 else if ([name isEqualToString:@"Upgrade"])
3196 [delegate_ installPackage:package_];
3197 else _assert(false);
3198 }
3199
3200 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3201 int count = [buttons_ count];
3202 _assert(count != 0);
3203 _assert(button <= count + 1);
3204
3205 if (count != button - 1)
3206 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3207
3208 [sheet dismiss];
3209 }
3210
3211 - (void) _rightButtonClicked {
3212 int count = [buttons_ count];
3213 _assert(count != 0);
3214
3215 if (count == 1)
3216 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3217 else {
3218 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3219 [buttons addObjectsFromArray:buttons_];
3220 [buttons addObject:@"Cancel"];
3221
3222 [delegate_ slideUp:[[[UIAlertSheet alloc]
3223 initWithTitle:nil
3224 buttons:buttons
3225 defaultButtonIndex:2
3226 delegate:self
3227 context:@"manage"
3228 ] autorelease]];
3229 }
3230 }
3231
3232 - (NSString *) rightButtonTitle {
3233 int count = [buttons_ count];
3234 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3235 }
3236
3237 - (NSString *) title {
3238 return @"Details";
3239 }
3240
3241 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3242 if ((self = [super initWithBook:book]) != nil) {
3243 database_ = database;
3244
3245 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3246 [self addSubview:table_];
3247
3248 [table_ setDataSource:self];
3249 [table_ setDelegate:self];
3250
3251 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3252 } return self;
3253 }
3254
3255 - (void) setPackage:(Package *)package {
3256 if (package_ != nil) {
3257 [package_ autorelease];
3258 package_ = nil;
3259 }
3260
3261 if (name_ != nil) {
3262 [name_ release];
3263 name_ = nil;
3264 }
3265
3266 if (description_ != nil) {
3267 [description_ release];
3268 description_ = nil;
3269 }
3270
3271 [buttons_ removeAllObjects];
3272
3273 if (package != nil) {
3274 package_ = [package retain];
3275 name_ = [[package id] retain];
3276
3277 NSString *description([package description]);
3278 if (description == nil)
3279 description = [package tagline];
3280 if (description != nil) {
3281 description_ = [GetTextView(description, 12, true) retain];
3282 [description_ setTextColor:Black_];
3283 }
3284
3285 [table_ reloadData];
3286
3287 if ([package_ source] == nil);
3288 else if ([package_ upgradableAndEssential:NO])
3289 [buttons_ addObject:@"Upgrade"];
3290 else if ([package_ installed] == nil)
3291 [buttons_ addObject:@"Install"];
3292 else
3293 [buttons_ addObject:@"Reinstall"];
3294 if ([package_ installed] != nil)
3295 [buttons_ addObject:@"Remove"];
3296 }
3297 }
3298
3299 - (void) resetViewAnimated:(BOOL)animated {
3300 [table_ resetViewAnimated:animated];
3301 }
3302
3303 - (void) reloadData {
3304 [self setPackage:[database_ packageWithName:name_]];
3305 [self reloadButtons];
3306 }
3307
3308 @end
3309 /* }}} */
3310 /* Package Table {{{ */
3311 @interface PackageTable : RVPage {
3312 _transient Database *database_;
3313 NSString *title_;
3314 SEL filter_;
3315 id object_;
3316 NSMutableArray *packages_;
3317 NSMutableArray *sections_;
3318 UISectionList *list_;
3319 }
3320
3321 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3322
3323 - (void) setDelegate:(id)delegate;
3324 - (void) setObject:(id)object;
3325
3326 - (void) reloadData;
3327 - (void) resetCursor;
3328
3329 - (UISectionList *) list;
3330
3331 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3332
3333 @end
3334
3335 @implementation PackageTable
3336
3337 - (void) dealloc {
3338 [list_ setDataSource:nil];
3339
3340 [title_ release];
3341 if (object_ != nil)
3342 [object_ release];
3343 [packages_ release];
3344 [sections_ release];
3345 [list_ release];
3346 [super dealloc];
3347 }
3348
3349 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3350 return [sections_ count];
3351 }
3352
3353 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3354 return [[sections_ objectAtIndex:section] name];
3355 }
3356
3357 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3358 return [[sections_ objectAtIndex:section] row];
3359 }
3360
3361 - (int) numberOfRowsInTable:(UITable *)table {
3362 return [packages_ count];
3363 }
3364
3365 - (float) table:(UITable *)table heightForRow:(int)row {
3366 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3367 }
3368
3369 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3370 if (reusing == nil)
3371 reusing = [[[PackageCell alloc] init] autorelease];
3372 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3373 return reusing;
3374 }
3375
3376 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3377 return NO;
3378 }
3379
3380 - (void) tableRowSelected:(NSNotification *)notification {
3381 int row = [[notification object] selectedRow];
3382 if (row == INT_MAX)
3383 return;
3384
3385 Package *package = [packages_ objectAtIndex:row];
3386 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3387 [view setDelegate:delegate_];
3388 [view setPackage:package];
3389 [book_ pushPage:view];
3390 }
3391
3392 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3393 if ((self = [super initWithBook:book]) != nil) {
3394 database_ = database;
3395 title_ = [title retain];
3396 filter_ = filter;
3397 object_ = object == nil ? nil : [object retain];
3398
3399 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3400 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3401
3402 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3403 [list_ setDataSource:self];
3404
3405 UITableColumn *column = [[[UITableColumn alloc]
3406 initWithTitle:@"Name"
3407 identifier:@"name"
3408 width:[self frame].size.width
3409 ] autorelease];
3410
3411 UITable *table = [list_ table];
3412 [table setSeparatorStyle:1];
3413 [table addTableColumn:column];
3414 [table setDelegate:self];
3415 [table setReusesTableCells:YES];
3416
3417 [self addSubview:list_];
3418 [self reloadData];
3419 } return self;
3420 }
3421
3422 - (void) setDelegate:(id)delegate {
3423 delegate_ = delegate;
3424 }
3425
3426 - (void) setObject:(id)object {
3427 if (object_ != nil)
3428 [object_ release];
3429 if (object == nil)
3430 object_ = nil;
3431 else
3432 object_ = [object retain];
3433 }
3434
3435 - (void) reloadData {
3436 NSArray *packages = [database_ packages];
3437
3438 [packages_ removeAllObjects];
3439 [sections_ removeAllObjects];
3440
3441 for (size_t i(0); i != [packages count]; ++i) {
3442 Package *package([packages objectAtIndex:i]);
3443 if ([[package performSelector:filter_ withObject:object_] boolValue])
3444 [packages_ addObject:package];
3445 }
3446
3447 Section *section = nil;
3448
3449 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3450 Package *package = [packages_ objectAtIndex:offset];
3451 NSString *name = [package index];
3452
3453 if (section == nil || ![[section name] isEqualToString:name]) {
3454 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3455 [sections_ addObject:section];
3456 }
3457
3458 [section addToCount];
3459 }
3460
3461 [list_ reloadData];
3462 }
3463
3464 - (NSString *) title {
3465 return title_;
3466 }
3467
3468 - (void) resetViewAnimated:(BOOL)animated {
3469 [list_ resetViewAnimated:animated];
3470 }
3471
3472 - (void) resetCursor {
3473 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3474 }
3475
3476 - (UISectionList *) list {
3477 return list_;
3478 }
3479
3480 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3481 [list_ setShouldHideHeaderInShortLists:hide];
3482 }
3483
3484 @end
3485 /* }}} */
3486
3487 /* Browser Implementation {{{ */
3488 @implementation BrowserView
3489
3490 - (void) dealloc {
3491 WebView *webview = [webview_ webView];
3492 [webview setFrameLoadDelegate:nil];
3493 [webview setResourceLoadDelegate:nil];
3494 [webview setUIDelegate:nil];
3495
3496 [scroller_ setDelegate:nil];
3497 [webview_ setDelegate:nil];
3498
3499 [scroller_ release];
3500 [webview_ release];
3501 [urls_ release];
3502 [indicator_ release];
3503 if (title_ != nil)
3504 [title_ release];
3505 [super dealloc];
3506 }
3507
3508 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3509 NSMutableURLRequest *request = [NSMutableURLRequest
3510 requestWithURL:url
3511 cachePolicy:policy
3512 timeoutInterval:30.0
3513 ];
3514
3515 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3516 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3517 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3518
3519 [self loadRequest:request];
3520 }
3521
3522
3523 - (void) loadURL:(NSURL *)url {
3524 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3525 }
3526
3527 // XXX: this needs to add the headers
3528 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3529 return request;
3530 }
3531
3532 - (void) loadRequest:(NSURLRequest *)request {
3533 pushed_ = true;
3534 [webview_ loadRequest:request];
3535 }
3536
3537 - (void) reloadURL {
3538 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3539 [urls_ removeLastObject];
3540 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3541 }
3542
3543 - (WebView *) webView {
3544 return [webview_ webView];
3545 }
3546
3547 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3548 [scroller_ setContentSize:frame.size];
3549 }
3550
3551 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3552 [self view:sender didSetFrame:frame];
3553 }
3554
3555 - (void) getAppTapp:(NSString *)href {
3556 if ([href hasPrefix:@"apptapp://package/"]) {
3557 NSString *name = [href substringFromIndex:18];
3558 Package *package = [database_ packageWithName:name];
3559 if (package == nil) {
3560 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3561 initWithTitle:@"Cannot Locate Package"
3562 buttons:[NSArray arrayWithObjects:@"Close", nil]
3563 defaultButtonIndex:0
3564 delegate:self
3565 context:@"missing"
3566 ] autorelease];
3567
3568 [sheet setBodyText:[NSString stringWithFormat:
3569 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3570 , name]];
3571
3572 [sheet popupAlertAnimated:YES];
3573 } else {
3574 [self setBackButtonTitle:title_];
3575 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3576 [view setDelegate:delegate_];
3577 [view setPackage:package];
3578 [book_ pushPage:view];
3579 }
3580 }
3581 }
3582
3583 - (void) webView:(WebView *)sender willClickElement:(id)element {
3584 if (![element respondsToSelector:@selector(href)])
3585 return;
3586 NSString *href = [element href];
3587 if (href == nil)
3588 return;
3589 if ([href hasPrefix:@"apptapp://package/"])
3590 [self getAppTapp:href];
3591 }
3592
3593 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3594 if ([[[request URL] scheme] isEqualToString:@"apptapp"]) {
3595 [self getAppTapp:[[request URL] absoluteString]];
3596 return nil;
3597 }
3598
3599 if (!pushed_) {
3600 pushed_ = true;
3601 [book_ pushPage:self];
3602 }
3603
3604 return [self _addHeadersToRequest:request];
3605 }
3606
3607 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3608 if (request != nil && [[[request URL] scheme] isEqualToString:@"apptapp"])
3609 return nil;
3610 else {
3611 [self setBackButtonTitle:title_];
3612
3613 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3614 [browser setDelegate:delegate_];
3615
3616 if (request != nil) {
3617 [browser loadRequest:[self _addHeadersToRequest:request]];
3618 [book_ pushPage:browser];
3619 }
3620
3621 return [browser webView];
3622 }
3623 }
3624
3625 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3626 title_ = [title retain];
3627 [self setTitle:title];
3628 }
3629
3630 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3631 if ([frame parentFrame] != nil)
3632 return;
3633
3634 reloading_ = false;
3635 loading_ = true;
3636 [indicator_ startAnimation];
3637 [self reloadButtons];
3638
3639 if (title_ != nil) {
3640 [title_ release];
3641 title_ = nil;
3642 }
3643
3644 [self setTitle:@"Loading..."];
3645
3646 WebView *webview = [webview_ webView];
3647 NSString *href = [webview mainFrameURL];
3648 [urls_ addObject:[NSURL URLWithString:href]];
3649
3650 CGRect webrect = [scroller_ frame];
3651 webrect.size.height = 0;
3652 [webview_ setFrame:webrect];
3653 }
3654
3655 - (void) _finishLoading {
3656 if (!reloading_) {
3657 loading_ = false;
3658 [indicator_ stopAnimation];
3659 [self reloadButtons];
3660 }
3661 }
3662
3663 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3664 if ([frame parentFrame] != nil)
3665 return;
3666 [self _finishLoading];
3667 }
3668
3669 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3670 if ([frame parentFrame] != nil)
3671 return;
3672 [self setTitle:[error localizedDescription]];
3673 [self _finishLoading];
3674 }
3675
3676 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3677 if ((self = [super initWithBook:book]) != nil) {
3678 database_ = database;
3679 loading_ = false;
3680
3681 struct CGRect bounds = [self bounds];
3682
3683 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3684 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3685 [self addSubview:pinstripe];
3686
3687 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3688 [self addSubview:scroller_];
3689
3690 [scroller_ setScrollingEnabled:YES];
3691 [scroller_ setAdjustForContentSizeChange:YES];
3692 [scroller_ setClipsSubviews:YES];
3693 [scroller_ setAllowsRubberBanding:YES];
3694 [scroller_ setScrollDecelerationFactor:0.99];
3695 [scroller_ setDelegate:self];
3696
3697 CGRect webrect = [scroller_ bounds];
3698 webrect.size.height = 0;
3699
3700 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3701 [scroller_ addSubview:webview_];
3702
3703 [webview_ setTilingEnabled:YES];
3704 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3705 [webview_ setAutoresizes:YES];
3706 [webview_ setDelegate:self];
3707 //[webview_ setEnabledGestures:2];
3708
3709 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
3710 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
3711 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
3712
3713 Package *package([database_ packageWithName:@"cydia"]);
3714 NSString *application = package == nil ? @"Cydia" : [NSString
3715 stringWithFormat:@"Cydia/%@",
3716 [package installed]
3717 ];
3718
3719 WebView *webview = [webview_ webView];
3720 [webview setApplicationNameForUserAgent:application];
3721 [webview setFrameLoadDelegate:self];
3722 [webview setResourceLoadDelegate:self];
3723 [webview setUIDelegate:self];
3724
3725 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3726 } return self;
3727 }
3728
3729 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3730 [sheet dismiss];
3731 }
3732
3733 - (void) _leftButtonClicked {
3734 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3735 initWithTitle:@"About Cydia Installer"
3736 buttons:[NSArray arrayWithObjects:@"Close", nil]
3737 defaultButtonIndex:0
3738 delegate:self
3739 context:@"about"
3740 ] autorelease];
3741
3742 [sheet setBodyText:
3743 @"Copyright (C) 2008\n"
3744 "Jay Freeman (saurik)\n"
3745 "saurik@saurik.com\n"
3746 "http://www.saurik.com/\n"
3747 "\n"
3748 "The Okori Group\n"
3749 "http://www.theokorigroup.com/\n"
3750 "\n"
3751 "College of Creative Studies,\n"
3752 "University of California,\n"
3753 "Santa Barbara\n"
3754 "http://www.ccs.ucsb.edu/"
3755 ];
3756
3757 [sheet popupAlertAnimated:YES];
3758 }
3759
3760 - (void) _rightButtonClicked {
3761 reloading_ = true;
3762 [self reloadURL];
3763 }
3764
3765 - (NSString *) leftButtonTitle {
3766 return @"About";
3767 }
3768
3769 - (NSString *) rightButtonTitle {
3770 return loading_ ? @"" : @"Reload";
3771 }
3772
3773 - (NSString *) title {
3774 return nil;
3775 }
3776
3777 - (NSString *) backButtonTitle {
3778 return @"Browser";
3779 }
3780
3781 - (void) setPageActive:(BOOL)active {
3782 if (active)
3783 [book_ addSubview:indicator_];
3784 else
3785 [indicator_ removeFromSuperview];
3786 }
3787
3788 - (void) resetViewAnimated:(BOOL)animated {
3789 }
3790
3791 - (void) setPushed:(bool)pushed {
3792 pushed_ = pushed;
3793 }
3794
3795 @end
3796 /* }}} */
3797
3798 @interface CYBook : RVBook <
3799 ProgressDelegate
3800 > {
3801 _transient Database *database_;
3802 UIView *overlay_;
3803 UIProgressIndicator *indicator_;
3804 UITextLabel *prompt_;
3805 UIProgressBar *progress_;
3806 bool updating_;
3807 }
3808
3809 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3810 - (void) update;
3811 - (BOOL) updating;
3812
3813 @end
3814
3815 /* Source Cell {{{ */
3816 @interface SourceCell : UITableCell {
3817 UITextLabel *description_;
3818 UIRightTextLabel *label_;
3819 UITextLabel *origin_;
3820 }
3821
3822 - (void) dealloc;
3823
3824 - (SourceCell *) initWithSource:(Source *)source;
3825
3826 - (void) _setSelected:(float)fraction;
3827 - (void) setSelected:(BOOL)selected;
3828 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
3829 - (void) _setSelectionFadeFraction:(float)fraction;
3830
3831 @end
3832
3833 @implementation SourceCell
3834
3835 - (void) dealloc {
3836 [description_ release];
3837 [label_ release];
3838 [origin_ release];
3839 [super dealloc];
3840 }
3841
3842 - (SourceCell *) initWithSource:(Source *)source {
3843 if ((self = [super init]) != nil) {
3844 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
3845 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
3846
3847 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
3848 float clear[] = {0, 0, 0, 0};
3849
3850 NSString *description = [source description];
3851 if (description == nil)
3852 description = [source uri];
3853
3854 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)];
3855 [description_ setBackgroundColor:CGColorCreate(space, clear)];
3856 [description_ setFont:bold];
3857 [description_ setText:description];
3858
3859 NSString *label = [source label];
3860 if (label == nil)
3861 label = [source type];
3862
3863 label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)];
3864 [label_ setBackgroundColor:CGColorCreate(space, clear)];
3865 [label_ setFont:small];
3866 [label_ setText:label];
3867
3868 NSString *origin = [source origin];
3869 if (origin == nil)
3870 origin = [source distribution];
3871
3872 origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
3873 [origin_ setBackgroundColor:CGColorCreate(space, clear)];
3874 [origin_ setFont:small];
3875 [origin_ setText:origin];
3876
3877 [self addSubview:description_];
3878 [self addSubview:label_];
3879 [self addSubview:origin_];
3880
3881 CFRelease(small);
3882 CFRelease(bold);
3883 } return self;
3884 }
3885
3886 - (void) _setSelected:(float)fraction {
3887 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
3888
3889 float black[] = {
3890 Interpolate(0.0, 1.0, fraction),
3891 Interpolate(0.0, 1.0, fraction),
3892 Interpolate(0.0, 1.0, fraction),
3893 1.0};
3894
3895 float blue[] = {
3896 Interpolate(0.2, 1.0, fraction),
3897 Interpolate(0.2, 1.0, fraction),
3898 Interpolate(1.0, 1.0, fraction),
3899 1.0};
3900
3901 float gray[] = {
3902 Interpolate(0.4, 1.0, fraction),
3903 Interpolate(0.4, 1.0, fraction),
3904 Interpolate(0.4, 1.0, fraction),
3905 1.0};
3906
3907 [description_ setColor:CGColorCreate(space, black)];
3908 [label_ setColor:CGColorCreate(space, blue)];
3909 [origin_ setColor:CGColorCreate(space, gray)];
3910 }
3911
3912 - (void) setSelected:(BOOL)selected {
3913 [self _setSelected:(selected ? 1.0 : 0.0)];
3914 [super setSelected:selected];
3915 }
3916
3917 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
3918 if (!fade)
3919 [self _setSelected:(selected ? 1.0 : 0.0)];
3920 [super setSelected:selected withFade:fade];
3921 }
3922
3923 - (void) _setSelectionFadeFraction:(float)fraction {
3924 [self _setSelected:fraction];
3925 [super _setSelectionFadeFraction:fraction];
3926 }
3927
3928 @end
3929 /* }}} */
3930 /* Source Table {{{ */
3931 @interface SourceTable : RVPage {
3932 _transient Database *database_;
3933 UISectionList *list_;
3934 NSMutableArray *sources_;
3935 UIAlertSheet *alert_;
3936 }
3937
3938 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3939
3940 @end
3941
3942 @implementation SourceTable
3943
3944 - (void) dealloc {
3945 [list_ setDataSource:nil];
3946
3947 if (sources_ != nil)
3948 [sources_ release];
3949 [list_ release];
3950 [super dealloc];
3951 }
3952
3953 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3954 return 1;
3955 }
3956
3957 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3958 return @"Sources";
3959 }
3960
3961 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3962 return 0;
3963 }
3964
3965 - (int) numberOfRowsInTable:(UITable *)table {
3966 return [sources_ count];
3967 }
3968
3969 - (float) table:(UITable *)table heightForRow:(int)row {
3970 return 64;
3971 }
3972
3973 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3974 return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease];
3975 }
3976
3977 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3978 return NO;
3979 }
3980
3981 - (void) tableRowSelected:(NSNotification*)notification {
3982 UITable *table([list_ table]);
3983 int row([table selectedRow]);
3984 if (row == INT_MAX)
3985 return;
3986
3987 [table selectRow:-1 byExtendingSelection:NO withFade:YES];
3988 }
3989
3990 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3991 if ((self = [super initWithBook:book]) != nil) {
3992 database_ = database;
3993 sources_ = nil;
3994
3995 list_ = [[UISectionList alloc] initWithFrame:[self bounds]];
3996
3997 [self addSubview:list_];
3998
3999 [list_ setDataSource:self];
4000 [list_ setShouldHideHeaderInShortLists:NO];
4001
4002 UITableColumn *column = [[UITableColumn alloc]
4003 initWithTitle:@"Name"
4004 identifier:@"name"
4005 width:[self frame].size.width
4006 ];
4007
4008 UITable *table = [list_ table];
4009 [table setSeparatorStyle:1];
4010 [table addTableColumn:column];
4011 [table setDelegate:self];
4012 } return self;
4013 }
4014
4015 - (void) reloadData {
4016 pkgSourceList list;
4017 _assert(list.ReadMainList());
4018
4019 if (sources_ != nil)
4020 [sources_ release];
4021
4022 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4023 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
4024 [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]];
4025
4026 [list_ reloadData];
4027 }
4028
4029 - (void) resetViewAnimated:(BOOL)animated {
4030 [list_ resetViewAnimated:animated];
4031 }
4032
4033 - (NSString *) leftTitle {
4034 return @"Refresh All";
4035 }
4036
4037 - (NSString *) rightTitle {
4038 return @"Edit";
4039 }
4040
4041 @end
4042 /* }}} */
4043
4044 /* Install View {{{ */
4045 @interface InstallView : RVPage {
4046 _transient Database *database_;
4047 NSMutableArray *packages_;
4048 NSMutableArray *sections_;
4049 UITable *list_;
4050 UIView *accessory_;
4051 }
4052
4053 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4054 - (void) reloadData;
4055
4056 @end
4057
4058 @implementation InstallView
4059
4060 - (void) dealloc {
4061 [list_ setDataSource:nil];
4062 [list_ setDelegate:nil];
4063
4064 [packages_ release];
4065 [sections_ release];
4066 [list_ release];
4067 [accessory_ release];
4068 [super dealloc];
4069 }
4070
4071 - (int) numberOfRowsInTable:(UITable *)table {
4072 return [sections_ count] + 1;
4073 }
4074
4075 - (float) table:(UITable *)table heightForRow:(int)row {
4076 return 45;
4077 }
4078
4079 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4080 if (reusing == nil)
4081 reusing = [[[SectionCell alloc] init] autorelease];
4082 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
4083 return reusing;
4084 }
4085
4086 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4087 return YES;
4088 }
4089
4090 - (void) tableRowSelected:(NSNotification *)notification {
4091 int row = [[notification object] selectedRow];
4092 if (row == INT_MAX)
4093 return;
4094
4095 Section *section;
4096 NSString *name;
4097 NSString *title;
4098
4099 if (row == 0) {
4100 section = nil;
4101 name = nil;
4102 title = @"All Packages";
4103 } else {
4104 section = [sections_ objectAtIndex:(row - 1)];
4105 name = [section name];
4106
4107 if (name != nil)
4108 title = name;
4109 else {
4110 name = @"";
4111 title = @"(No Section)";
4112 }
4113 }
4114
4115 PackageTable *table = [[[PackageTable alloc]
4116 initWithBook:book_
4117 database:database_
4118 title:title
4119 filter:@selector(isUninstalledInSection:)
4120 with:name
4121 ] autorelease];
4122
4123 [table setDelegate:delegate_];
4124
4125 [book_ pushPage:table];
4126 }
4127
4128 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4129 if ((self = [super initWithBook:book]) != nil) {
4130 database_ = database;
4131
4132 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4133 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4134
4135 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4136 [self addSubview:list_];
4137
4138 UITableColumn *column = [[[UITableColumn alloc]
4139 initWithTitle:@"Name"
4140 identifier:@"name"
4141 width:[self frame].size.width
4142 ] autorelease];
4143
4144 [list_ setDataSource:self];
4145 [list_ setSeparatorStyle:1];
4146 [list_ addTableColumn:column];
4147 [list_ setDelegate:self];
4148 [list_ setReusesTableCells:YES];
4149
4150 [self reloadData];
4151 } return self;
4152 }
4153
4154 - (void) reloadData {
4155 NSArray *packages = [database_ packages];
4156
4157 [packages_ removeAllObjects];
4158 [sections_ removeAllObjects];
4159
4160 for (size_t i(0); i != [packages count]; ++i) {
4161 Package *package([packages objectAtIndex:i]);
4162 if ([package valid] && [package installed] == nil)
4163 [packages_ addObject:package];
4164 }
4165
4166 [packages_ sortUsingSelector:@selector(compareBySection:)];
4167
4168 Section *section = nil;
4169 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4170 Package *package = [packages_ objectAtIndex:offset];
4171 NSString *name = [package section];
4172
4173 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4174 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4175 [sections_ addObject:section];
4176 }
4177
4178 [section addToCount];
4179 }
4180
4181 [list_ reloadData];
4182 }
4183
4184 - (void) resetViewAnimated:(BOOL)animated {
4185 [list_ resetViewAnimated:animated];
4186 }
4187
4188 - (NSString *) title {
4189 return @"Install";
4190 }
4191
4192 - (NSString *) backButtonTitle {
4193 return @"Sections";
4194 }
4195
4196 - (UIView *) accessoryView {
4197 return accessory_;
4198 }
4199
4200 @end
4201 /* }}} */
4202 /* Changes View {{{ */
4203 @interface ChangesView : RVPage {
4204 _transient Database *database_;
4205 NSMutableArray *packages_;
4206 NSMutableArray *sections_;
4207 UISectionList *list_;
4208 unsigned upgrades_;
4209 }
4210
4211 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4212 - (void) reloadData;
4213
4214 @end
4215
4216 @implementation ChangesView
4217
4218 - (void) dealloc {
4219 [[list_ table] setDelegate:nil];
4220 [list_ setDataSource:nil];
4221
4222 [packages_ release];
4223 [sections_ release];
4224 [list_ release];
4225 [super dealloc];
4226 }
4227
4228 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4229 return [sections_ count];
4230 }
4231
4232 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4233 return [[sections_ objectAtIndex:section] name];
4234 }
4235
4236 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4237 return [[sections_ objectAtIndex:section] row];
4238 }
4239
4240 - (int) numberOfRowsInTable:(UITable *)table {
4241 return [packages_ count];
4242 }
4243
4244 - (float) table:(UITable *)table heightForRow:(int)row {
4245 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4246 }
4247
4248 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4249 if (reusing == nil)
4250 reusing = [[[PackageCell alloc] init] autorelease];
4251 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4252 return reusing;
4253 }
4254
4255 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4256 return NO;
4257 }
4258
4259 - (void) tableRowSelected:(NSNotification *)notification {
4260 int row = [[notification object] selectedRow];
4261 if (row == INT_MAX)
4262 return;
4263 Package *package = [packages_ objectAtIndex:row];
4264 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4265 [view setDelegate:delegate_];
4266 [view setPackage:package];
4267 [book_ pushPage:view];
4268 }
4269
4270 - (void) _leftButtonClicked {
4271 [(CYBook *)book_ update];
4272 [self reloadButtons];
4273 }
4274
4275 - (void) _rightButtonClicked {
4276 [delegate_ distUpgrade];
4277 }
4278
4279 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4280 if ((self = [super initWithBook:book]) != nil) {
4281 database_ = database;
4282
4283 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4284 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4285
4286 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4287 [self addSubview:list_];
4288
4289 [list_ setShouldHideHeaderInShortLists:NO];
4290 [list_ setDataSource:self];
4291 //[list_ setSectionListStyle:1];
4292
4293 UITableColumn *column = [[[UITableColumn alloc]
4294 initWithTitle:@"Name"
4295 identifier:@"name"
4296 width:[self frame].size.width
4297 ] autorelease];
4298
4299 UITable *table = [list_ table];
4300 [table setSeparatorStyle:1];
4301 [table addTableColumn:column];
4302 [table setDelegate:self];
4303 [table setReusesTableCells:YES];
4304
4305 [self reloadData];
4306 } return self;
4307 }
4308
4309 - (void) reloadData {
4310 NSArray *packages = [database_ packages];
4311
4312 [packages_ removeAllObjects];
4313 [sections_ removeAllObjects];
4314
4315 for (size_t i(0); i != [packages count]; ++i) {
4316 Package *package([packages objectAtIndex:i]);
4317 if ([package installed] == nil && [package valid] || [package upgradableAndEssential:NO])
4318 [packages_ addObject:package];
4319 }
4320
4321 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4322
4323 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
4324 Section *section = nil;
4325
4326 upgrades_ = 0;
4327 bool unseens = false;
4328
4329 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4330
4331 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4332 Package *package = [packages_ objectAtIndex:offset];
4333
4334 if ([package upgradableAndEssential:YES]) {
4335 ++upgrades_;
4336 [upgradable addToCount];
4337 } else {
4338 unseens = true;
4339 NSDate *seen = [package seen];
4340
4341 NSString *name;
4342
4343 if (seen == nil)
4344 name = [@"n/a ?" retain];
4345 else {
4346 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4347 }
4348
4349 if (section == nil || ![[section name] isEqualToString:name]) {
4350 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4351 [sections_ addObject:section];
4352 }
4353
4354 [name release];
4355 [section addToCount];
4356 }
4357 }
4358
4359 CFRelease(formatter);
4360
4361 if (unseens) {
4362 Section *last = [sections_ lastObject];
4363 size_t count = [last count];
4364 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4365 [sections_ removeLastObject];
4366 }
4367
4368 if (upgrades_ != 0)
4369 [sections_ insertObject:upgradable atIndex:0];
4370
4371 [list_ reloadData];
4372 [self reloadButtons];
4373 }
4374
4375 - (void) resetViewAnimated:(BOOL)animated {
4376 [list_ resetViewAnimated:animated];
4377 }
4378
4379 - (NSString *) leftButtonTitle {
4380 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4381 }
4382
4383 - (NSString *) rightButtonTitle {
4384 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
4385 }
4386
4387 - (NSString *) title {
4388 return @"Changes";
4389 }
4390
4391 @end
4392 /* }}} */
4393 /* Manage View {{{ */
4394 @interface ManageView : RVPage {
4395 _transient Database *database_;
4396 PackageTable *packages_;
4397 SourceTable *sources_;
4398 }
4399
4400 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4401
4402 @end
4403
4404 @implementation ManageView
4405
4406 - (void) dealloc {
4407 [packages_ release];
4408 [sources_ release];
4409 [super dealloc];
4410 }
4411
4412 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4413 if ((self = [super initWithBook:book]) != nil) {
4414 database_ = database;
4415
4416 packages_ = [[PackageTable alloc]
4417 initWithBook:book
4418 database:database
4419 title:nil
4420 filter:@selector(isInstalledInSection:)
4421 with:nil
4422 ];
4423
4424 sources_ = [[SourceTable alloc]
4425 initWithBook:book
4426 database:database
4427 ];
4428
4429 [self addSubview:packages_];
4430 } return self;
4431 }
4432
4433 - (void) resetViewAnimated:(BOOL)animated {
4434 [packages_ resetViewAnimated:animated];
4435 [sources_ resetViewAnimated:animated];
4436 }
4437
4438 - (void) reloadData {
4439 [packages_ reloadData];
4440 [sources_ reloadData];
4441 }
4442
4443 - (NSString *) title {
4444 return @"Installed Packages";
4445 }
4446
4447 - (NSString *) backButtonTitle {
4448 return @"Packages";
4449 }
4450
4451 - (void) setDelegate:(id)delegate {
4452 [super setDelegate:delegate];
4453 [packages_ setDelegate:delegate];
4454 [sources_ setDelegate:delegate];
4455 }
4456
4457 @end
4458 /* }}} */
4459 /* Search View {{{ */
4460 @protocol SearchViewDelegate
4461 - (void) showKeyboard:(BOOL)show;
4462 @end
4463
4464 @interface SearchView : RVPage {
4465 UIView *accessory_;
4466 UISearchField *field_;
4467 UITransitionView *transition_;
4468 PackageTable *table_;
4469 UIPreferencesTable *advanced_;
4470 UIView *dimmed_;
4471 bool flipped_;
4472 bool reload_;
4473 }
4474
4475 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4476 - (void) reloadData;
4477
4478 @end
4479
4480 @implementation SearchView
4481
4482 - (void) dealloc {
4483 #ifndef __OBJC2__
4484 [[field_ textTraits] setEditingDelegate:nil];
4485 #endif
4486 [field_ setDelegate:nil];
4487
4488 [accessory_ release];
4489 [field_ release];
4490 [transition_ release];
4491 [table_ release];
4492 [advanced_ release];
4493 [dimmed_ release];
4494 [super dealloc];
4495 }
4496
4497 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4498 return 1;
4499 }
4500
4501 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4502 switch (group) {
4503 case 0: return @"Advanced Search (Coming Soon!)";
4504
4505 default: _assert(false);
4506 }
4507 }
4508
4509 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4510 switch (group) {
4511 case 0: return 0;
4512
4513 default: _assert(false);
4514 }
4515 }
4516
4517 - (void) _showKeyboard:(BOOL)show {
4518 CGSize keysize = [UIKeyboard defaultSize];
4519 CGRect keydown = [book_ pageBounds];
4520 CGRect keyup = keydown;
4521 keyup.size.height -= keysize.height - ButtonBarHeight_;
4522
4523 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4524
4525 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4526 [animation setSignificantRectFields:8];
4527
4528 if (show) {
4529 [animation setStartFrame:keydown];
4530 [animation setEndFrame:keyup];
4531 } else {
4532 [animation setStartFrame:keyup];
4533 [animation setEndFrame:keydown];
4534 }
4535
4536 UIAnimator *animator = [UIAnimator sharedAnimator];
4537
4538 [animator
4539 addAnimations:[NSArray arrayWithObjects:animation, nil]
4540 withDuration:(KeyboardTime_ - delay)
4541 start:!show
4542 ];
4543
4544 if (show)
4545 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4546
4547 #ifndef __OBJC2__
4548 [delegate_ showKeyboard:show];
4549 #endif
4550 }
4551
4552 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
4553 [self _showKeyboard:YES];
4554 }
4555
4556 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
4557 [self _showKeyboard:NO];
4558 }
4559
4560 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
4561 if (reload_) {
4562 NSString *text([field_ text]);
4563 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4564 [self reloadData];
4565 reload_ = false;
4566 }
4567 }
4568
4569 - (void) textFieldClearButtonPressed:(UITextField *)field {
4570 reload_ = true;
4571 }
4572
4573 - (void) keyboardInputShouldDelete:(id)input {
4574 reload_ = true;
4575 }
4576
4577 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
4578 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4579 reload_ = true;
4580 return YES;
4581 } else {
4582 [field_ resignFirstResponder];
4583 return NO;
4584 }
4585 }
4586
4587 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4588 if ((self = [super initWithBook:book]) != nil) {
4589 CGRect pageBounds = [book_ pageBounds];
4590
4591 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
4592 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4593 [self addSubview:pinstripe];*/
4594
4595 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4596 [self addSubview:transition_];
4597
4598 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4599
4600 [advanced_ setReusesTableCells:YES];
4601 [advanced_ setDataSource:self];
4602 [advanced_ reloadData];
4603
4604 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4605 CGColor dimmed(space_, 0, 0, 0, 0.5);
4606 [dimmed_ setBackgroundColor:dimmed];
4607
4608 table_ = [[PackageTable alloc]
4609 initWithBook:book
4610 database:database
4611 title:nil
4612 filter:@selector(isSearchedForBy:)
4613 with:nil
4614 ];
4615
4616 [table_ setShouldHideHeaderInShortLists:NO];
4617 [transition_ transition:0 toView:table_];
4618
4619 CGRect cnfrect = {{
4620 #ifdef __OBJC2__
4621 6 +
4622 #endif
4623 1, 38}, {17, 18}};
4624
4625 CGRect area;
4626 area.origin.x = cnfrect.origin.x + cnfrect.size.width + 14;
4627 area.origin.y = 30;
4628
4629 area.size.width =
4630 #ifdef __OBJC2__
4631 8 +
4632 #endif
4633 [self bounds].size.width - area.origin.x - 18;
4634
4635 area.size.height = [UISearchField defaultHeight];
4636
4637 field_ = [[UISearchField alloc] initWithFrame:area];
4638
4639 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4640 [field_ setFont:font];
4641 CFRelease(font);
4642
4643 [field_ setPlaceholder:@"Package Names & Descriptions"];
4644 [field_ setDelegate:self];
4645
4646 #ifdef __OBJC2__
4647 [field_ setPaddingTop:3];
4648 #else
4649 [field_ setPaddingTop:5];
4650 #endif
4651
4652 #ifndef __OBJC2__
4653 UITextTraits *traits = [field_ textTraits];
4654 [traits setEditingDelegate:self];
4655 [traits setReturnKeyType:6];
4656 [traits setAutoCapsType:0];
4657 [traits setAutoCorrectionType:1];
4658 #endif
4659
4660 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
4661
4662 accessory_ = [[UIView alloc] initWithFrame:accrect];
4663 [accessory_ addSubview:field_];
4664
4665 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4666 [configure setShowPressFeedback:YES];
4667 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
4668 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
4669 [accessory_ addSubview:configure];
4670 } return self;
4671 }
4672
4673 - (void) flipPage {
4674 #ifndef __OBJC2__
4675 LKAnimation *animation = [LKTransition animation];
4676 [animation setType:@"oglFlip"];
4677 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4678 [animation setFillMode:@"extended"];
4679 [animation setTransitionFlags:3];
4680 [animation setDuration:10];
4681 [animation setSpeed:0.35];
4682 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4683 [[transition_ _layer] addAnimation:animation forKey:0];
4684 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4685 flipped_ = !flipped_;
4686 #endif
4687 }
4688
4689 - (void) configurePushed {
4690 [field_ resignFirstResponder];
4691 [self flipPage];
4692 }
4693
4694 - (void) resetViewAnimated:(BOOL)animated {
4695 if (flipped_)
4696 [self flipPage];
4697 [table_ resetViewAnimated:animated];
4698 }
4699
4700 - (void) reloadData {
4701 if (flipped_)
4702 [self flipPage];
4703 [table_ setObject:[field_ text]];
4704 [table_ reloadData];
4705 [table_ resetCursor];
4706 }
4707
4708 - (UIView *) accessoryView {
4709 return accessory_;
4710 }
4711
4712 - (NSString *) title {
4713 return nil;
4714 }
4715
4716 - (NSString *) backButtonTitle {
4717 return @"Search";
4718 }
4719
4720 - (void) setDelegate:(id)delegate {
4721 [table_ setDelegate:delegate];
4722 [super setDelegate:delegate];
4723 }
4724
4725 @end
4726 /* }}} */
4727
4728 @implementation CYBook
4729
4730 - (void) dealloc {
4731 [overlay_ release];
4732 [indicator_ release];
4733 [prompt_ release];
4734 [progress_ release];
4735 [super dealloc];
4736 }
4737
4738 - (NSString *) getTitleForPage:(RVPage *)page {
4739 return Simplify([super getTitleForPage:page]);
4740 }
4741
4742 - (BOOL) updating {
4743 return updating_;
4744 }
4745
4746 - (void) update {
4747 [navbar_ setPrompt:@""];
4748 [navbar_ addSubview:overlay_];
4749 [indicator_ startAnimation];
4750 [prompt_ setText:@"Updating Database..."];
4751 [progress_ setProgress:0];
4752
4753 updating_ = true;
4754
4755 [NSThread
4756 detachNewThreadSelector:@selector(_update)
4757 toTarget:self
4758 withObject:nil
4759 ];
4760 }
4761
4762 - (void) _update_ {
4763 updating_ = false;
4764
4765 [overlay_ removeFromSuperview];
4766 [indicator_ stopAnimation];
4767 [delegate_ reloadData];
4768
4769 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
4770 }
4771
4772 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4773 if ((self = [super initWithFrame:frame]) != nil) {
4774 database_ = database;
4775
4776 if (Advanced_)
4777 [navbar_ setBarStyle:1];
4778
4779 CGRect ovrrect = [navbar_ bounds];
4780 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
4781
4782 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
4783
4784 UIProgressIndicatorStyle style = Advanced_ ?
4785 kUIProgressIndicatorStyleSmallWhite :
4786 kUIProgressIndicatorStyleSmallBlack;
4787
4788 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4789 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4790 CGRect indrect = {{indoffset, indoffset}, indsize};
4791
4792 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4793 [indicator_ setStyle:style];
4794 [overlay_ addSubview:indicator_];
4795
4796 CGSize prmsize = {200, indsize.width + 4};
4797
4798 CGRect prmrect = {{
4799 indoffset * 2 + indsize.width,
4800 #ifdef __OBJC2__
4801 -1 +
4802 #endif
4803 (ovrrect.size.height - prmsize.height) / 2
4804 }, prmsize};
4805
4806 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
4807
4808 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4809
4810 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
4811 [prompt_ setBackgroundColor:Clear_];
4812 [prompt_ setFont:font];
4813
4814 CFRelease(font);
4815
4816 [overlay_ addSubview:prompt_];
4817
4818 CGSize prgsize = {75, 100};
4819
4820 CGRect prgrect = {{
4821 ovrrect.size.width - prgsize.width - 10,
4822 (ovrrect.size.height - prgsize.height) / 2
4823 } , prgsize};
4824
4825 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4826 [progress_ setStyle:0];
4827 [overlay_ addSubview:progress_];
4828 } return self;
4829 }
4830
4831 - (void) _update {
4832 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4833
4834 Status status;
4835 status.setDelegate(self);
4836
4837 [database_ updateWithStatus:status];
4838
4839 [self
4840 performSelectorOnMainThread:@selector(_update_)
4841 withObject:nil
4842 waitUntilDone:NO
4843 ];
4844
4845 [pool release];
4846 }
4847
4848 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
4849 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4850 }
4851
4852 - (void) setProgressTitle:(NSString *)title {
4853 [self
4854 performSelectorOnMainThread:@selector(_setProgressTitle:)
4855 withObject:title
4856 waitUntilDone:YES
4857 ];
4858 }
4859
4860 - (void) setProgressPercent:(float)percent {
4861 }
4862
4863 - (void) addProgressOutput:(NSString *)output {
4864 [self
4865 performSelectorOnMainThread:@selector(_addProgressOutput:)
4866 withObject:output
4867 waitUntilDone:YES
4868 ];
4869 }
4870
4871 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4872 [sheet dismiss];
4873 }
4874
4875 - (void) _setProgressTitle:(NSString *)title {
4876 [prompt_ setText:[title stringByAppendingString:@"..."]];
4877 }
4878
4879 - (void) _addProgressOutput:(NSString *)output {
4880 }
4881
4882 @end
4883
4884 @interface Cydia : UIApplication <
4885 ConfirmationViewDelegate,
4886 ProgressViewDelegate,
4887 SearchViewDelegate,
4888 CydiaDelegate
4889 > {
4890 UIWindow *window_;
4891
4892 UIView *underlay_;
4893 UIView *overlay_;
4894 CYBook *book_;
4895 UIButtonBar *buttonbar_;
4896
4897 ConfirmationView *confirm_;
4898
4899 NSMutableArray *essential_;
4900 NSMutableArray *broken_;
4901
4902 Database *database_;
4903 ProgressView *progress_;
4904
4905 unsigned tag_;
4906
4907 UIKeyboard *keyboard_;
4908 UIProgressHUD *hud_;
4909
4910 InstallView *install_;
4911 ChangesView *changes_;
4912 ManageView *manage_;
4913 SearchView *search_;
4914 }
4915
4916 @end
4917
4918 @implementation Cydia
4919
4920 - (void) _loaded {
4921 if ([broken_ count] != 0) {
4922 int count = [broken_ count];
4923
4924 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4925 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
4926 buttons:[NSArray arrayWithObjects:
4927 @"Forcibly Clear",
4928 @"Ignore (Temporary)",
4929 nil]
4930 defaultButtonIndex:0
4931 delegate:self
4932 context:@"fixhalf"
4933 ] autorelease];
4934
4935 [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."];
4936 [sheet popupAlertAnimated:YES];
4937 } else if (!Ignored_ && [essential_ count] != 0) {
4938 int count = [essential_ count];
4939
4940 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4941 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
4942 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
4943 defaultButtonIndex:0
4944 delegate:self
4945 context:@"upgrade"
4946 ] autorelease];
4947
4948 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
4949 [sheet popupAlertAnimated:YES];
4950 }
4951 }
4952
4953 - (void) _reloadData {
4954 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4955 [hud setText:@"Reloading Data"];
4956 [overlay_ addSubview:hud];
4957 [hud show:YES];*/
4958
4959 [database_ reloadData];
4960
4961 if (Packages_ == nil) {
4962 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
4963 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4964 }
4965
4966 size_t changes(0);
4967
4968 [essential_ removeAllObjects];
4969 [broken_ removeAllObjects];
4970
4971 NSArray *packages = [database_ packages];
4972 for (int i(0), e([packages count]); i != e; ++i) {
4973 Package *package = [packages objectAtIndex:i];
4974 if ([package half])
4975 [broken_ addObject:package];
4976 if ([package upgradableAndEssential:NO]) {
4977 if ([package essential])
4978 [essential_ addObject:package];
4979 ++changes;
4980 }
4981 }
4982
4983 if (changes != 0) {
4984 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4985 [buttonbar_ setBadgeValue:badge forButton:3];
4986 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4987 [buttonbar_ setBadgeAnimated:YES forButton:3];
4988 [self setApplicationBadge:badge];
4989 } else {
4990 [buttonbar_ setBadgeValue:nil forButton:3];
4991 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4992 [buttonbar_ setBadgeAnimated:NO forButton:3];
4993 [self removeApplicationBadge];
4994 }
4995
4996 if (Changed_) {
4997 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4998 Changed_ = false;
4999 }
5000
5001 /* XXX: this is just stupid */
5002 if (tag_ != 2)
5003 [install_ reloadData];
5004 if (tag_ != 3)
5005 [changes_ reloadData];
5006 if (tag_ != 4)
5007 [manage_ reloadData];
5008 if (tag_ != 5)
5009 [search_ reloadData];
5010
5011 [book_ reloadData];
5012
5013 if ([packages count] == 0);
5014 else if (Loaded_)
5015 [self _loaded];
5016 else {
5017 Loaded_ = YES;
5018 [book_ update];
5019 }
5020
5021 /*[hud show:NO];
5022 [hud removeFromSuperview];*/
5023 }
5024
5025 - (void) reloadData {
5026 @synchronized (self) {
5027 if (confirm_ == nil)
5028 [self _reloadData];
5029 }
5030 }
5031
5032 - (void) resolve {
5033 pkgProblemResolver *resolver = [database_ resolver];
5034
5035 resolver->InstallProtect();
5036 if (!resolver->Resolve(true))
5037 _error->Discard();
5038 }
5039
5040 - (void) perform {
5041 [database_ prepare];
5042
5043 if ([database_ cache]->BrokenCount() == 0)
5044 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5045 else {
5046 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5047 NSArray *packages = [database_ packages];
5048
5049 for (size_t i(0); i != [packages count]; ++i) {
5050 Package *package = [packages objectAtIndex:i];
5051 if ([package broken])
5052 [broken addObject:[package name]];
5053 }
5054
5055 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5056 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5057 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5058 defaultButtonIndex:0
5059 delegate:self
5060 context:@"broken"
5061 ] autorelease];
5062
5063 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5064 [sheet popupAlertAnimated:YES];
5065
5066 [self _reloadData];
5067 }
5068 }
5069
5070 - (void) installPackage:(Package *)package {
5071 @synchronized (self) {
5072 [package install];
5073 [self resolve];
5074 [self perform];
5075 }
5076 }
5077
5078 - (void) removePackage:(Package *)package {
5079 @synchronized (self) {
5080 [package remove];
5081 [self resolve];
5082 [self perform];
5083 }
5084 }
5085
5086 - (void) distUpgrade {
5087 @synchronized (self) {
5088 [database_ upgrade];
5089 [self perform];
5090 }
5091 }
5092
5093 - (void) cancel {
5094 @synchronized (self) {
5095 [confirm_ release];
5096 confirm_ = nil;
5097 [self _reloadData];
5098 }
5099 }
5100
5101 - (void) confirm {
5102 [overlay_ removeFromSuperview];
5103 reload_ = true;
5104
5105 [progress_
5106 detachNewThreadSelector:@selector(perform)
5107 toTarget:database_
5108 withObject:nil
5109 title:@"Running..."
5110 ];
5111 }
5112
5113 - (void) bootstrap_ {
5114 [database_ update];
5115 [database_ upgrade];
5116 [database_ prepare];
5117 [database_ perform];
5118 }
5119
5120 - (void) bootstrap {
5121 [progress_
5122 detachNewThreadSelector:@selector(bootstrap_)
5123 toTarget:self
5124 withObject:nil
5125 title:@"Bootstrap Install..."
5126 ];
5127 }
5128
5129 - (void) progressViewIsComplete:(ProgressView *)progress {
5130 @synchronized (self) {
5131 [self _reloadData];
5132
5133 if (confirm_ != nil) {
5134 [underlay_ addSubview:overlay_];
5135 [confirm_ removeFromSuperview];
5136 [confirm_ release];
5137 confirm_ = nil;
5138 }
5139 }
5140 }
5141
5142 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5143 NSString *context = [sheet context];
5144 if ([context isEqualToString:@"fixhalf"])
5145 switch (button) {
5146 case 1:
5147 @synchronized (self) {
5148 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5149 Package *broken = [broken_ objectAtIndex:i];
5150 [broken remove];
5151
5152 NSString *id = [broken id];
5153 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5154 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5155 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5156 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5157 }
5158
5159 [self resolve];
5160 [self perform];
5161 }
5162 break;
5163
5164 case 2:
5165 [broken_ removeAllObjects];
5166 [self _loaded];
5167 break;
5168
5169 default:
5170 _assert(false);
5171 }
5172 else if ([context isEqualToString:@"upgrade"])
5173 switch (button) {
5174 case 1:
5175 @synchronized (self) {
5176 for (int i = 0, e = [essential_ count]; i != e; ++i) {
5177 Package *essential = [essential_ objectAtIndex:i];
5178 [essential install];
5179 }
5180
5181 [self resolve];
5182 [self perform];
5183 }
5184 break;
5185
5186 case 2:
5187 Ignored_ = YES;
5188 break;
5189
5190 default:
5191 _assert(false);
5192 }
5193
5194 [sheet dismiss];
5195 }
5196
5197 - (void) setPage:(RVPage *)page {
5198 [page resetViewAnimated:NO];
5199 [page setDelegate:self];
5200 [book_ setPage:page];
5201 }
5202
5203 - (RVPage *) _setHomePage {
5204 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
5205 [self setPage:browser];
5206 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
5207 return browser;
5208 }
5209
5210 - (void) buttonBarItemTapped:(id)sender {
5211 unsigned tag = [sender tag];
5212 if (tag == tag_) {
5213 [book_ resetViewAnimated:YES];
5214 return;
5215 }
5216
5217 switch (tag) {
5218 case 1: [self _setHomePage]; break;
5219
5220 case 2: [self setPage:install_]; break;
5221 case 3: [self setPage:changes_]; break;
5222 case 4: [self setPage:manage_]; break;
5223 case 5: [self setPage:search_]; break;
5224
5225 default: _assert(false);
5226 }
5227
5228 tag_ = tag;
5229 }
5230
5231 - (void) applicationWillSuspend {
5232 _trace();
5233 [super applicationWillSuspend];
5234
5235 [database_ clean];
5236
5237 if (reload_) {
5238 pid_t pid = ExecFork();
5239 if (pid == 0) {
5240 #ifndef __OBJC2__
5241 sleep(1);
5242 #endif
5243
5244 if (pid_t child = fork()) {
5245 waitpid(child, NULL, 0);
5246 } else {
5247 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5248 perror("launchctl unload");
5249 exit(0);
5250 }
5251
5252 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5253 perror("launchctl load");
5254 exit(0);
5255 }
5256 }
5257 }
5258
5259 - (void) finish {
5260 if (hud_ != nil) {
5261 [self setStatusBarShowsProgress:NO];
5262
5263 [hud_ show:NO];
5264 [hud_ removeFromSuperview];
5265 [hud_ autorelease];
5266 hud_ = nil;
5267
5268 reload_ = true;
5269 [self suspendWithAnimation:YES];
5270 return;
5271 }
5272
5273 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5274
5275 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5276 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5277 0, 0, screenrect.size.width, screenrect.size.height - 48
5278 ) database:database_];
5279
5280 [book_ setDelegate:self];
5281
5282 [overlay_ addSubview:book_];
5283
5284 NSArray *buttonitems = [NSArray arrayWithObjects:
5285 [NSDictionary dictionaryWithObjectsAndKeys:
5286 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5287 @"home-up.png", kUIButtonBarButtonInfo,
5288 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5289 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5290 self, kUIButtonBarButtonTarget,
5291 @"Home", kUIButtonBarButtonTitle,
5292 @"0", kUIButtonBarButtonType,
5293 nil],
5294
5295 [NSDictionary dictionaryWithObjectsAndKeys:
5296 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5297 @"install-up.png", kUIButtonBarButtonInfo,
5298 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5299 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5300 self, kUIButtonBarButtonTarget,
5301 @"Install", kUIButtonBarButtonTitle,
5302 @"0", kUIButtonBarButtonType,
5303 nil],
5304
5305 [NSDictionary dictionaryWithObjectsAndKeys:
5306 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5307 @"changes-up.png", kUIButtonBarButtonInfo,
5308 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5309 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5310 self, kUIButtonBarButtonTarget,
5311 @"Changes", kUIButtonBarButtonTitle,
5312 @"0", kUIButtonBarButtonType,
5313 nil],
5314
5315 [NSDictionary dictionaryWithObjectsAndKeys:
5316 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5317 @"manage-up.png", kUIButtonBarButtonInfo,
5318 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5319 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5320 self, kUIButtonBarButtonTarget,
5321 @"Manage", kUIButtonBarButtonTitle,
5322 @"0", kUIButtonBarButtonType,
5323 nil],
5324
5325 [NSDictionary dictionaryWithObjectsAndKeys:
5326 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5327 @"search-up.png", kUIButtonBarButtonInfo,
5328 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5329 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5330 self, kUIButtonBarButtonTarget,
5331 @"Search", kUIButtonBarButtonTitle,
5332 @"0", kUIButtonBarButtonType,
5333 nil],
5334 nil];
5335
5336 buttonbar_ = [[UIButtonBar alloc]
5337 initInView:overlay_
5338 withFrame:CGRectMake(
5339 0, screenrect.size.height - ButtonBarHeight_,
5340 screenrect.size.width, ButtonBarHeight_
5341 )
5342 withItemList:buttonitems
5343 ];
5344
5345 [buttonbar_ setDelegate:self];
5346 [buttonbar_ setBarStyle:1];
5347 [buttonbar_ setButtonBarTrackingMode:2];
5348
5349 int buttons[5] = {1, 2, 3, 4, 5};
5350 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5351 [buttonbar_ showButtonGroup:0 withDuration:0];
5352
5353 for (int i = 0; i != 5; ++i)
5354 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5355 i * 64 + 2, 1, 60, ButtonBarHeight_
5356 )];
5357
5358 [buttonbar_ showSelectionForButton:1];
5359 [overlay_ addSubview:buttonbar_];
5360
5361 [UIKeyboard initImplementationNow];
5362 CGSize keysize = [UIKeyboard defaultSize];
5363 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5364 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5365 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5366 [overlay_ addSubview:keyboard_];
5367
5368 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5369 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5370 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
5371 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5372
5373 if (!bootstrap_)
5374 [underlay_ addSubview:overlay_];
5375
5376 [self reloadData];
5377
5378 if (bootstrap_)
5379 [self bootstrap];
5380 else
5381 [self _setHomePage];
5382 }
5383
5384 - (void) reorganize {
5385 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5386 system("/usr/libexec/cydia/free.sh");
5387 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
5388 [pool release];
5389 }
5390
5391 - (void) applicationSuspend:(__GSEvent *)event {
5392 if (hud_ == nil && ![progress_ isRunning])
5393 [super applicationSuspend:event];
5394 _trace();
5395 }
5396
5397 - (void) applicationDidFinishLaunching:(id)unused {
5398 _assert(pkgInitConfig(*_config));
5399 _assert(pkgInitSystem(*_config, _system));
5400
5401 confirm_ = nil;
5402 tag_ = 1;
5403
5404 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
5405 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
5406
5407 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5408 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
5409
5410 [window_ orderFront: self];
5411 [window_ makeKey: self];
5412 [window_ _setHidden: NO];
5413
5414 database_ = [[Database alloc] init];
5415 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
5416 [database_ setDelegate:progress_];
5417 [window_ setContentView:progress_];
5418
5419 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
5420 [progress_ setContentView:underlay_];
5421
5422 [progress_ resetView];
5423
5424 if (
5425 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
5426 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL ||
5427 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
5428 readlink("/Library/Wallpapers", NULL, 0) == -1 && errno == EINVAL
5429 ) {
5430 hud_ = [[UIProgressHUD alloc] initWithWindow:window_];
5431 [hud_ setText:@"Reorganizing\nOne Minute!\nPlease Wait...\nDO NOT STOP"];
5432 [hud_ show:YES];
5433 [underlay_ addSubview:hud_];
5434
5435 [self setStatusBarShowsProgress:YES];
5436
5437 [NSThread
5438 detachNewThreadSelector:@selector(reorganize)
5439 toTarget:self
5440 withObject:nil
5441 ];
5442 } else
5443 [self finish];
5444 }
5445
5446 - (void) showKeyboard:(BOOL)show {
5447 CGSize keysize = [UIKeyboard defaultSize];
5448 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
5449 CGRect keyup = keydown;
5450 keyup.origin.y -= keysize.height;
5451
5452 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
5453 [animation setSignificantRectFields:2];
5454
5455 if (show) {
5456 [animation setStartFrame:keydown];
5457 [animation setEndFrame:keyup];
5458 [keyboard_ activate];
5459 } else {
5460 [animation setStartFrame:keyup];
5461 [animation setEndFrame:keydown];
5462 [keyboard_ deactivate];
5463 }
5464
5465 [[UIAnimator sharedAnimator]
5466 addAnimations:[NSArray arrayWithObjects:animation, nil]
5467 withDuration:KeyboardTime_
5468 start:YES
5469 ];
5470 }
5471
5472 - (void) slideUp:(UIAlertSheet *)alert {
5473 if (Advanced_)
5474 [alert presentSheetFromButtonBar:buttonbar_];
5475 else
5476 [alert presentSheetInView:overlay_];
5477 }
5478
5479 @end
5480
5481 void AddPreferences(NSString *plist) {
5482 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5483
5484 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
5485 _assert(settings != NULL);
5486 NSMutableArray *items = [settings objectForKey:@"items"];
5487
5488 bool cydia(false);
5489
5490 for (size_t i(0); i != [items count]; ++i) {
5491 NSMutableDictionary *item([items objectAtIndex:i]);
5492 NSString *label = [item objectForKey:@"label"];
5493 if (label != nil && [label isEqualToString:@"Cydia"]) {
5494 cydia = true;
5495 break;
5496 }
5497 }
5498
5499 if (!cydia) {
5500 for (size_t i(0); i != [items count]; ++i) {
5501 NSDictionary *item([items objectAtIndex:i]);
5502 NSString *label = [item objectForKey:@"label"];
5503 if (label != nil && [label isEqualToString:@"General"]) {
5504 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
5505 @"CydiaSettings", @"bundle",
5506 @"PSLinkCell", @"cell",
5507 [NSNumber numberWithBool:YES], @"hasIcon",
5508 [NSNumber numberWithBool:YES], @"isController",
5509 @"Cydia", @"label",
5510 nil] atIndex:(i + 1)];
5511
5512 break;
5513 }
5514 }
5515
5516 _assert([settings writeToFile:plist atomically:YES] == YES);
5517 }
5518
5519 [pool release];
5520 }
5521
5522 /*IMP alloc_;
5523 id Alloc_(id self, SEL selector) {
5524 id object = alloc_(self, selector);
5525 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
5526 return object;
5527 }*/
5528
5529 /*IMP dealloc_;
5530 id Dealloc_(id self, SEL selector) {
5531 id object = dealloc_(self, selector);
5532 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
5533 return object;
5534 }*/
5535
5536 int main(int argc, char *argv[]) {
5537 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5538
5539 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5540
5541 Home_ = NSHomeDirectory();
5542
5543 {
5544 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5545 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5546 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5547 Sounds_Keyboard_ = [keyboard boolValue];
5548 }
5549
5550 setuid(0);
5551 setgid(0);
5552
5553 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
5554 _assert(errno == ENOENT);
5555 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
5556 _assert(errno == ENOENT);
5557
5558 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5559 alloc_ = alloc->method_imp;
5560 alloc->method_imp = (IMP) &Alloc_;*/
5561
5562 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5563 dealloc_ = dealloc->method_imp;
5564 dealloc->method_imp = (IMP) &Dealloc_;*/
5565
5566 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5567 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5568 Firmware_ = strdup([prover UTF8String]);
5569 NSArray *versions = [prover componentsSeparatedByString:@"."];
5570 int count = [versions count];
5571 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
5572 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5573 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
5574 }
5575 }
5576
5577 size_t size;
5578 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5579 char *machine = new char[size];
5580 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5581 Machine_ = machine;
5582
5583 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5584 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5585 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5586 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5587 CFRelease(serial);
5588 }
5589
5590 IOObjectRelease(service);
5591 }
5592
5593 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5594 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
5595
5596 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5597 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5598 else
5599 Packages_ = [Metadata_ objectForKey:@"Packages"];
5600
5601 if (access("/User", F_OK) != 0)
5602 system("/usr/libexec/cydia/firmware.sh");
5603
5604 Locale_ = CFLocaleCopyCurrent();
5605 space_ = CGColorSpaceCreateDeviceRGB();
5606
5607 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
5608 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5609 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5610 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5611 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5612
5613 int value = UIApplicationMain(argc, argv, [Cydia class]);
5614
5615 CGColorSpaceRelease(space_);
5616 CFRelease(Locale_);
5617
5618 [pool release];
5619 return value;
5620 }