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