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