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