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