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