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