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