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