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