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