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