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