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