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