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