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