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