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