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