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