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