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