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