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