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