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