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