]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Fixed some minor reported bugs.
[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 _trace();
2330 [packages_ sortUsingSelector:@selector(compareByName:)];
2331 _trace();
2332 }
2333
2334 - (void) configure {
2335 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2336 system([dpkg UTF8String]);
2337 }
2338
2339 - (void) clean {
2340 if (lock_ != NULL)
2341 return;
2342
2343 FileFd Lock;
2344 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2345 _assert(!_error->PendingError());
2346
2347 pkgAcquire fetcher;
2348 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2349
2350 class LogCleaner :
2351 public pkgArchiveCleaner
2352 {
2353 protected:
2354 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2355 unlink(File);
2356 }
2357 } cleaner;
2358
2359 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2360 std::string error;
2361 while (_error->PopMessage(error))
2362 lprintf("ArchiveCleaner: %s\n", error.c_str());
2363 }
2364 }
2365
2366 - (void) prepare {
2367 pkgRecords records(cache_);
2368
2369 lock_ = new FileFd();
2370 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2371 _assert(!_error->PendingError());
2372
2373 pkgSourceList list;
2374 // XXX: explain this with an error message
2375 _assert(list.ReadMainList());
2376
2377 manager_ = (_system->CreatePM(cache_));
2378 _assert(manager_->GetArchives(fetcher_, &list, &records));
2379 _assert(!_error->PendingError());
2380 }
2381
2382 - (void) perform {
2383 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2384 pkgSourceList list;
2385 _assert(list.ReadMainList());
2386 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2387 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2388 }
2389
2390 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2391 _trace();
2392 return;
2393 }
2394
2395 bool failed = false;
2396 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2397 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2398 continue;
2399
2400 std::string uri = (*item)->DescURI();
2401 std::string error = (*item)->ErrorText;
2402
2403 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2404 failed = true;
2405
2406 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2407 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2408 waitUntilDone:YES
2409 ];
2410 }
2411
2412 if (failed) {
2413 _trace();
2414 return;
2415 }
2416
2417 _system->UnLock();
2418 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2419
2420 if (_error->PendingError()) {
2421 _trace();
2422 return;
2423 }
2424
2425 if (result == pkgPackageManager::Failed) {
2426 _trace();
2427 return;
2428 }
2429
2430 if (result != pkgPackageManager::Completed) {
2431 _trace();
2432 return;
2433 }
2434
2435 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2436 pkgSourceList list;
2437 _assert(list.ReadMainList());
2438 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2439 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2440 }
2441
2442 if (![before isEqualToArray:after])
2443 [self update];
2444 }
2445
2446 - (void) upgrade {
2447 _assert(pkgDistUpgrade(cache_));
2448 }
2449
2450 - (void) update {
2451 [self updateWithStatus:status_];
2452 }
2453
2454 - (void) updateWithStatus:(Status &)status {
2455 pkgSourceList list;
2456 _assert(list.ReadMainList());
2457
2458 FileFd lock;
2459 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2460 _assert(!_error->PendingError());
2461
2462 pkgAcquire fetcher(&status);
2463 _assert(list.GetIndexes(&fetcher));
2464
2465 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2466 bool failed = false;
2467 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2468 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2469 (*item)->Finished();
2470 failed = true;
2471 }
2472
2473 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2474 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2475 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2476 }
2477
2478 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2479 Changed_ = true;
2480 }
2481 }
2482
2483 - (void) setDelegate:(id)delegate {
2484 delegate_ = delegate;
2485 status_.setDelegate(delegate);
2486 progress_.setDelegate(delegate);
2487 }
2488
2489 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2490 pkgIndexFile *index(NULL);
2491 list_->FindIndex(file, index);
2492 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2493 }
2494
2495 @end
2496 /* }}} */
2497
2498 /* PopUp Windows {{{ */
2499 @interface PopUpView : UIView {
2500 _transient id delegate_;
2501 UITransitionView *transition_;
2502 UIView *overlay_;
2503 }
2504
2505 - (void) cancel;
2506 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2507
2508 @end
2509
2510 @implementation PopUpView
2511
2512 - (void) dealloc {
2513 [transition_ setDelegate:nil];
2514 [transition_ release];
2515 [overlay_ release];
2516 [super dealloc];
2517 }
2518
2519 - (void) cancel {
2520 [transition_ transition:UITransitionPushFromTop toView:nil];
2521 }
2522
2523 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2524 if (from != nil && to == nil)
2525 [self removeFromSuperview];
2526 }
2527
2528 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2529 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2530 delegate_ = delegate;
2531
2532 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2533 [self addSubview:transition_];
2534
2535 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2536
2537 [view addSubview:self];
2538
2539 [transition_ setDelegate:self];
2540
2541 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2542 [transition_ transition:UITransitionNone toView:blank];
2543 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2544 } return self;
2545 }
2546
2547 @end
2548 /* }}} */
2549
2550 /* Mail Composition {{{ */
2551 @interface MailToView : PopUpView {
2552 MailComposeController *controller_;
2553 }
2554
2555 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2556
2557 @end
2558
2559 @implementation MailToView
2560
2561 - (void) dealloc {
2562 [controller_ release];
2563 [super dealloc];
2564 }
2565
2566 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2567 NSLog(@"will");
2568 }
2569
2570 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2571 NSLog(@"did:%@", delivery);
2572 // [UIApp setStatusBarShowsProgress:NO];
2573 if ([controller error]){
2574 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2575 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2576 [mailAlertSheet setBodyText:[controller error]];
2577 [mailAlertSheet popupAlertAnimated:YES];
2578 }
2579 }
2580
2581 - (void) showError {
2582 NSLog(@"%@", [controller_ error]);
2583 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2584 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2585 [mailAlertSheet setBodyText:[controller_ error]];
2586 [mailAlertSheet popupAlertAnimated:YES];
2587 }
2588
2589 - (void) deliverMessage { _pooled
2590 setuid(501);
2591 setgid(501);
2592
2593 if (![controller_ deliverMessage])
2594 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2595 }
2596
2597 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2598 if ([controller_ needsDelivery])
2599 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2600 else
2601 [self cancel];
2602 }
2603
2604 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2605 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2606 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2607 [controller_ setDelegate:self];
2608 [controller_ initializeUI];
2609 [controller_ setupForURL:url];
2610
2611 UIView *view([controller_ view]);
2612 [overlay_ addSubview:view];
2613 } return self;
2614 }
2615
2616 @end
2617 /* }}} */
2618 /* Confirmation View {{{ */
2619 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2620 if (!iterator.end())
2621 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2622 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2623 continue;
2624 pkgCache::PkgIterator package(dep.TargetPkg());
2625 if (package.end())
2626 continue;
2627 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2628 return true;
2629 }
2630
2631 return false;
2632 }
2633
2634 @protocol ConfirmationViewDelegate
2635 - (void) cancel;
2636 - (void) confirm;
2637 @end
2638
2639 @interface ConfirmationView : BrowserView {
2640 _transient Database *database_;
2641 UIActionSheet *essential_;
2642 NSArray *changes_;
2643 NSArray *issues_;
2644 NSArray *sizes_;
2645 BOOL substrate_;
2646 }
2647
2648 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2649
2650 @end
2651
2652 @implementation ConfirmationView
2653
2654 - (void) dealloc {
2655 [changes_ release];
2656 if (issues_ != nil)
2657 [issues_ release];
2658 [sizes_ release];
2659 if (essential_ != nil)
2660 [essential_ release];
2661 [super dealloc];
2662 }
2663
2664 - (void) cancel {
2665 [delegate_ cancel];
2666 [book_ popFromSuperviewAnimated:YES];
2667 }
2668
2669 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2670 NSString *context = [sheet context];
2671
2672 if ([context isEqualToString:@"remove"])
2673 switch (button) {
2674 case 1:
2675 [self cancel];
2676 break;
2677 case 2:
2678 if (substrate_)
2679 Finish_ = 2;
2680 [delegate_ confirm];
2681 break;
2682 default:
2683 _assert(false);
2684 }
2685 else if ([context isEqualToString:@"unable"])
2686 [self cancel];
2687
2688 [sheet dismiss];
2689 }
2690
2691 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2692 [window setValue:changes_ forKey:@"changes"];
2693 [window setValue:issues_ forKey:@"issues"];
2694 [window setValue:sizes_ forKey:@"sizes"];
2695 [super webView:sender didClearWindowObject:window forFrame:frame];
2696 }
2697
2698 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2699 if ((self = [super initWithBook:book]) != nil) {
2700 database_ = database;
2701
2702 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2703 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2704 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2705 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2706 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2707
2708 bool remove(false);
2709
2710 pkgDepCache::Policy *policy([database_ policy]);
2711
2712 pkgCacheFile &cache([database_ cache]);
2713 NSArray *packages = [database_ packages];
2714 for (size_t i(0), e = [packages count]; i != e; ++i) {
2715 Package *package = [packages objectAtIndex:i];
2716 pkgCache::PkgIterator iterator = [package iterator];
2717 pkgDepCache::StateCache &state(cache[iterator]);
2718
2719 NSString *name([package name]);
2720
2721 if (state.NewInstall())
2722 [installing addObject:name];
2723 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2724 [reinstalling addObject:name];
2725 else if (state.Upgrade())
2726 [upgrading addObject:name];
2727 else if (state.Downgrade())
2728 [downgrading addObject:name];
2729 else if (state.Delete()) {
2730 if ([package essential])
2731 remove = true;
2732 [removing addObject:name];
2733 } else continue;
2734
2735 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2736 substrate_ |= DepSubstrate(iterator.CurrentVer());
2737 }
2738
2739 if (!remove)
2740 essential_ = nil;
2741 else if (Advanced_ || true) {
2742 essential_ = [[UIActionSheet alloc]
2743 initWithTitle:@"Removing Essentials"
2744 buttons:[NSArray arrayWithObjects:
2745 @"Cancel Operation (Safe)",
2746 @"Force Removal (Unsafe)",
2747 nil]
2748 defaultButtonIndex:0
2749 delegate:self
2750 context:@"remove"
2751 ];
2752
2753 #ifndef __OBJC2__
2754 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2755 #endif
2756 [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."];
2757 } else {
2758 essential_ = [[UIActionSheet alloc]
2759 initWithTitle:@"Unable to Comply"
2760 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2761 defaultButtonIndex:0
2762 delegate:self
2763 context:@"unable"
2764 ];
2765
2766 [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."];
2767 }
2768
2769 changes_ = [[NSArray alloc] initWithObjects:
2770 installing,
2771 reinstalling,
2772 upgrading,
2773 downgrading,
2774 removing,
2775 nil];
2776
2777 issues_ = [database_ issues];
2778 if (issues_ != nil)
2779 issues_ = [issues_ retain];
2780
2781 sizes_ = [[NSArray alloc] initWithObjects:
2782 SizeString([database_ fetcher].FetchNeeded()),
2783 SizeString([database_ fetcher].PartialPresent()),
2784 SizeString([database_ cache]->UsrSize()),
2785 nil];
2786
2787 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2788 } return self;
2789 }
2790
2791 - (NSString *) backButtonTitle {
2792 return @"Confirm";
2793 }
2794
2795 - (NSString *) leftButtonTitle {
2796 return @"Cancel";
2797 }
2798
2799 - (NSString *) _rightButtonTitle {
2800 return issues_ == nil ? @"Confirm" : nil;
2801 }
2802
2803 - (void) _leftButtonClicked {
2804 [self cancel];
2805 }
2806
2807 #if !AlwaysReload
2808 - (void) _rightButtonClicked {
2809 if (essential_ != nil)
2810 [essential_ popupAlertAnimated:YES];
2811 else {
2812 if (substrate_)
2813 Finish_ = 2;
2814 [delegate_ confirm];
2815 }
2816 }
2817 #endif
2818
2819 @end
2820 /* }}} */
2821
2822 /* Progress Data {{{ */
2823 @interface ProgressData : NSObject {
2824 SEL selector_;
2825 id target_;
2826 id object_;
2827 }
2828
2829 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2830
2831 - (SEL) selector;
2832 - (id) target;
2833 - (id) object;
2834 @end
2835
2836 @implementation ProgressData
2837
2838 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2839 if ((self = [super init]) != nil) {
2840 selector_ = selector;
2841 target_ = target;
2842 object_ = object;
2843 } return self;
2844 }
2845
2846 - (SEL) selector {
2847 return selector_;
2848 }
2849
2850 - (id) target {
2851 return target_;
2852 }
2853
2854 - (id) object {
2855 return object_;
2856 }
2857
2858 @end
2859 /* }}} */
2860 /* Progress View {{{ */
2861 @interface ProgressView : UIView <
2862 ConfigurationDelegate,
2863 ProgressDelegate
2864 > {
2865 _transient Database *database_;
2866 UIView *view_;
2867 UIView *background_;
2868 UITransitionView *transition_;
2869 UIView *overlay_;
2870 UINavigationBar *navbar_;
2871 UIProgressBar *progress_;
2872 UITextView *output_;
2873 UITextLabel *status_;
2874 UIPushButton *close_;
2875 id delegate_;
2876 BOOL running_;
2877 SHA1SumValue springlist_;
2878 SHA1SumValue sandplate_;
2879 size_t received_;
2880 NSTimeInterval last_;
2881 }
2882
2883 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2884
2885 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2886 - (void) setContentView:(UIView *)view;
2887 - (void) resetView;
2888
2889 - (void) _retachThread;
2890 - (void) _detachNewThreadData:(ProgressData *)data;
2891 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2892
2893 - (BOOL) isRunning;
2894
2895 @end
2896
2897 @protocol ProgressViewDelegate
2898 - (void) progressViewIsComplete:(ProgressView *)sender;
2899 @end
2900
2901 @implementation ProgressView
2902
2903 - (void) dealloc {
2904 [transition_ setDelegate:nil];
2905 [navbar_ setDelegate:nil];
2906
2907 [view_ release];
2908 if (background_ != nil)
2909 [background_ release];
2910 [transition_ release];
2911 [overlay_ release];
2912 [navbar_ release];
2913 [progress_ release];
2914 [output_ release];
2915 [status_ release];
2916 [close_ release];
2917 [super dealloc];
2918 }
2919
2920 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2921 if (bootstrap_ && from == overlay_ && to == view_)
2922 exit(0);
2923 }
2924
2925 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2926 if ((self = [super initWithFrame:frame]) != nil) {
2927 database_ = database;
2928 delegate_ = delegate;
2929
2930 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2931 [transition_ setDelegate:self];
2932
2933 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2934
2935 if (bootstrap_)
2936 [overlay_ setBackgroundColor:[UIColor blackColor]];
2937 else {
2938 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2939 [background_ setBackgroundColor:[UIColor blackColor]];
2940 [self addSubview:background_];
2941 }
2942
2943 [self addSubview:transition_];
2944
2945 CGSize navsize = [UINavigationBar defaultSize];
2946 CGRect navrect = {{0, 0}, navsize};
2947
2948 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2949 [overlay_ addSubview:navbar_];
2950
2951 [navbar_ setBarStyle:1];
2952 [navbar_ setDelegate:self];
2953
2954 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2955 [navbar_ pushNavigationItem:navitem];
2956
2957 CGRect bounds = [overlay_ bounds];
2958 CGSize prgsize = [UIProgressBar defaultSize];
2959
2960 CGRect prgrect = {{
2961 (bounds.size.width - prgsize.width) / 2,
2962 bounds.size.height - prgsize.height - 20
2963 }, prgsize};
2964
2965 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2966 [progress_ setStyle:0];
2967
2968 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2969 10,
2970 bounds.size.height - prgsize.height - 50,
2971 bounds.size.width - 20,
2972 24
2973 )];
2974
2975 [status_ setColor:[UIColor whiteColor]];
2976 [status_ setBackgroundColor:[UIColor clearColor]];
2977
2978 [status_ setCentersHorizontally:YES];
2979 //[status_ setFont:font];
2980
2981 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2982 10,
2983 navrect.size.height + 20,
2984 bounds.size.width - 20,
2985 bounds.size.height - navsize.height - 62 - navrect.size.height
2986 )];
2987
2988 //[output_ setTextFont:@"Courier New"];
2989 [output_ setTextSize:12];
2990
2991 [output_ setTextColor:[UIColor whiteColor]];
2992 [output_ setBackgroundColor:[UIColor clearColor]];
2993
2994 [output_ setMarginTop:0];
2995 [output_ setAllowsRubberBanding:YES];
2996 [output_ setEditable:NO];
2997
2998 [overlay_ addSubview:output_];
2999
3000 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3001 10,
3002 bounds.size.height - prgsize.height - 50,
3003 bounds.size.width - 20,
3004 32 + prgsize.height
3005 )];
3006
3007 [close_ setAutosizesToFit:NO];
3008 [close_ setDrawsShadow:YES];
3009 [close_ setStretchBackground:YES];
3010 [close_ setEnabled:YES];
3011
3012 UIFont *bold = [UIFont boldSystemFontOfSize:22];
3013 [close_ setTitleFont:bold];
3014
3015 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3016 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3017 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
3018 } return self;
3019 }
3020
3021 - (void) setContentView:(UIView *)view {
3022 view_ = [view retain];
3023 }
3024
3025 - (void) resetView {
3026 [transition_ transition:6 toView:view_];
3027 }
3028
3029 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3030 NSString *context = [sheet context];
3031 if ([context isEqualToString:@"conffile"]) {
3032 FILE *input = [database_ input];
3033
3034 switch (button) {
3035 case 1:
3036 fprintf(input, "N\n");
3037 fflush(input);
3038 break;
3039 case 2:
3040 fprintf(input, "Y\n");
3041 fflush(input);
3042 break;
3043 default:
3044 _assert(false);
3045 }
3046 }
3047
3048 [sheet dismiss];
3049 }
3050
3051 - (void) closeButtonPushed {
3052 running_ = NO;
3053
3054 switch (Finish_) {
3055 case 0:
3056 [self resetView];
3057 break;
3058
3059 case 1:
3060 [delegate_ suspendWithAnimation:YES];
3061 break;
3062
3063 case 2:
3064 system("launchctl stop com.apple.SpringBoard");
3065 break;
3066
3067 case 3:
3068 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
3069 break;
3070
3071 case 4:
3072 system("reboot");
3073 break;
3074 }
3075 }
3076
3077 - (void) _retachThread {
3078 UINavigationItem *item = [navbar_ topItem];
3079 [item setTitle:@"Complete"];
3080
3081 [overlay_ addSubview:close_];
3082 [progress_ removeFromSuperview];
3083 [status_ removeFromSuperview];
3084
3085 [delegate_ progressViewIsComplete:self];
3086
3087 if (Finish_ < 4) {
3088 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3089 MMap mmap(file, MMap::ReadOnly);
3090 SHA1Summation sha1;
3091 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3092 if (!(sandplate_ == sha1.Result()))
3093 Finish_ = 4;
3094 }
3095
3096 if (Finish_ < 3) {
3097 FileFd file(SpringBoard_, FileFd::ReadOnly);
3098 MMap mmap(file, MMap::ReadOnly);
3099 SHA1Summation sha1;
3100 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3101 if (!(springlist_ == sha1.Result()))
3102 Finish_ = 3;
3103 }
3104
3105 switch (Finish_) {
3106 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3107 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3108 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3109 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3110 case 4: [close_ setTitle:@"Reboot Device"]; break;
3111 }
3112
3113 #define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3114
3115 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
3116 [cache autorelease];
3117
3118 NSFileManager *manager = [NSFileManager defaultManager];
3119 NSError *error = nil;
3120
3121 id system = [cache objectForKey:@"System"];
3122 if (system == nil)
3123 goto error;
3124
3125 struct stat info;
3126 if (stat(Cache_, &info) == -1)
3127 goto error;
3128
3129 [system removeAllObjects];
3130
3131 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
3132 for (NSString *app in apps)
3133 if ([app hasSuffix:@".app"]) {
3134 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3135 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3136 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3137 [info autorelease];
3138 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3139 [info setObject:path forKey:@"Path"];
3140 [info setObject:@"System" forKey:@"ApplicationType"];
3141 [system addInfoDictionary:info];
3142 }
3143 }
3144 }
3145 } else goto error;
3146
3147 [cache writeToFile:@Cache_ atomically:YES];
3148
3149 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3150 goto error;
3151 if (chmod(Cache_, info.st_mode) == -1)
3152 goto error;
3153
3154 if (false) error:
3155 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
3156 }
3157
3158 notify_post("com.apple.mobile.application_installed");
3159
3160 [delegate_ setStatusBarShowsProgress:NO];
3161 }
3162
3163 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
3164 [[data target] performSelector:[data selector] withObject:[data object]];
3165 [data release];
3166
3167 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
3168 }
3169
3170 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
3171 UINavigationItem *item = [navbar_ topItem];
3172 [item setTitle:title];
3173
3174 [status_ setText:nil];
3175 [output_ setText:@""];
3176 [progress_ setProgress:0];
3177
3178 received_ = 0;
3179 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3180
3181 [close_ removeFromSuperview];
3182 [overlay_ addSubview:progress_];
3183 [overlay_ addSubview:status_];
3184
3185 [delegate_ setStatusBarShowsProgress:YES];
3186 running_ = YES;
3187
3188 {
3189 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3190 MMap mmap(file, MMap::ReadOnly);
3191 SHA1Summation sha1;
3192 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3193 sandplate_ = sha1.Result();
3194 }
3195
3196 {
3197 FileFd file(SpringBoard_, FileFd::ReadOnly);
3198 MMap mmap(file, MMap::ReadOnly);
3199 SHA1Summation sha1;
3200 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3201 springlist_ = sha1.Result();
3202 }
3203
3204 [transition_ transition:6 toView:overlay_];
3205
3206 [NSThread
3207 detachNewThreadSelector:@selector(_detachNewThreadData:)
3208 toTarget:self
3209 withObject:[[ProgressData alloc]
3210 initWithSelector:selector
3211 target:target
3212 object:object
3213 ]
3214 ];
3215 }
3216
3217 - (void) repairWithSelector:(SEL)selector {
3218 [self
3219 detachNewThreadSelector:selector
3220 toTarget:database_
3221 withObject:nil
3222 title:@"Repairing"
3223 ];
3224 }
3225
3226 - (void) setConfigurationData:(NSString *)data {
3227 [self
3228 performSelectorOnMainThread:@selector(_setConfigurationData:)
3229 withObject:data
3230 waitUntilDone:YES
3231 ];
3232 }
3233
3234 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3235 Package *package = id == nil ? nil : [database_ packageWithName:id];
3236
3237 UIActionSheet *sheet = [[[UIActionSheet alloc]
3238 initWithTitle:(package == nil ? id : [package name])
3239 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3240 defaultButtonIndex:0
3241 delegate:self
3242 context:@"error"
3243 ] autorelease];
3244
3245 [sheet setBodyText:error];
3246 [sheet popupAlertAnimated:YES];
3247 }
3248
3249 - (void) setProgressTitle:(NSString *)title {
3250 [self
3251 performSelectorOnMainThread:@selector(_setProgressTitle:)
3252 withObject:title
3253 waitUntilDone:YES
3254 ];
3255 }
3256
3257 - (void) setProgressPercent:(float)percent {
3258 [self
3259 performSelectorOnMainThread:@selector(_setProgressPercent:)
3260 withObject:[NSNumber numberWithFloat:percent]
3261 waitUntilDone:YES
3262 ];
3263 }
3264
3265 - (void) startProgress {
3266 last_ = [NSDate timeIntervalSinceReferenceDate];
3267 }
3268
3269 - (void) addProgressOutput:(NSString *)output {
3270 [self
3271 performSelectorOnMainThread:@selector(_addProgressOutput:)
3272 withObject:output
3273 waitUntilDone:YES
3274 ];
3275 }
3276
3277 - (bool) isCancelling:(size_t)received {
3278 if (last_ != 0) {
3279 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3280 if (received_ != received) {
3281 received_ = received;
3282 last_ = now;
3283 } else if (now - last_ > 30)
3284 return true;
3285 }
3286
3287 return false;
3288 }
3289
3290 - (void) _setConfigurationData:(NSString *)data {
3291 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3292
3293 _assert(conffile_r(data));
3294
3295 NSString *ofile = conffile_r[1];
3296 //NSString *nfile = conffile_r[2];
3297
3298 UIActionSheet *sheet = [[[UIActionSheet alloc]
3299 initWithTitle:@"Configuration Upgrade"
3300 buttons:[NSArray arrayWithObjects:
3301 @"Keep My Old Copy",
3302 @"Accept The New Copy",
3303 // XXX: @"See What Changed",
3304 nil]
3305 defaultButtonIndex:0
3306 delegate:self
3307 context:@"conffile"
3308 ] autorelease];
3309
3310 [sheet setBodyText:[NSString stringWithFormat:
3311 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3312 , ofile]];
3313
3314 [sheet popupAlertAnimated:YES];
3315 }
3316
3317 - (void) _setProgressTitle:(NSString *)title {
3318 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3319 for (size_t i(0), e([words count]); i != e; ++i) {
3320 NSString *word([words objectAtIndex:i]);
3321 if (Package *package = [database_ packageWithName:word])
3322 [words replaceObjectAtIndex:i withObject:[package name]];
3323 }
3324
3325 [status_ setText:[words componentsJoinedByString:@" "]];
3326 }
3327
3328 - (void) _setProgressPercent:(NSNumber *)percent {
3329 [progress_ setProgress:[percent floatValue]];
3330 }
3331
3332 - (void) _addProgressOutput:(NSString *)output {
3333 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3334 CGSize size = [output_ contentSize];
3335 CGRect rect = {{0, size.height}, {size.width, 0}};
3336 [output_ scrollRectToVisible:rect animated:YES];
3337 }
3338
3339 - (BOOL) isRunning {
3340 return running_;
3341 }
3342
3343 @end
3344 /* }}} */
3345
3346 /* Package Cell {{{ */
3347 @interface PackageCell : UISimpleTableCell {
3348 UIImage *icon_;
3349 NSString *name_;
3350 NSString *description_;
3351 NSString *source_;
3352 UIImage *badge_;
3353 #ifdef USE_BADGES
3354 UITextLabel *status_;
3355 #endif
3356 }
3357
3358 - (PackageCell *) init;
3359 - (void) setPackage:(Package *)package;
3360
3361 + (int) heightForPackage:(Package *)package;
3362
3363 @end
3364
3365 @implementation PackageCell
3366
3367 - (void) clearPackage {
3368 if (icon_ != nil) {
3369 [icon_ release];
3370 icon_ = nil;
3371 }
3372
3373 if (name_ != nil) {
3374 [name_ release];
3375 name_ = nil;
3376 }
3377
3378 if (description_ != nil) {
3379 [description_ release];
3380 description_ = nil;
3381 }
3382
3383 if (source_ != nil) {
3384 [source_ release];
3385 source_ = nil;
3386 }
3387
3388 if (badge_ != nil) {
3389 [badge_ release];
3390 badge_ = nil;
3391 }
3392 }
3393
3394 - (void) dealloc {
3395 [self clearPackage];
3396 #ifdef USE_BADGES
3397 [status_ release];
3398 #endif
3399 [super dealloc];
3400 }
3401
3402 - (PackageCell *) init {
3403 if ((self = [super init]) != nil) {
3404 #ifdef USE_BADGES
3405 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
3406 [status_ setBackgroundColor:[UIColor clearColor]];
3407 [status_ setFont:small];
3408 #endif
3409 } return self;
3410 }
3411
3412 - (void) setPackage:(Package *)package {
3413 [self clearPackage];
3414
3415 Source *source = [package source];
3416 NSString *section = [package simpleSection];
3417
3418 icon_ = [[package icon] retain];
3419
3420 name_ = [[package name] retain];
3421 description_ = [[package tagline] retain];
3422
3423 NSString *label = nil;
3424 bool trusted = false;
3425
3426 if (source != nil) {
3427 label = [source label];
3428 trusted = [source trusted];
3429 } else if ([[package id] isEqualToString:@"firmware"])
3430 label = @"Apple";
3431 else
3432 label = @"Unknown/Local";
3433
3434 NSString *from = [NSString stringWithFormat:@"from %@", label];
3435
3436 if (section != nil && ![section isEqualToString:label])
3437 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3438
3439 source_ = [from retain];
3440
3441 if (NSString *purpose = [package primaryPurpose])
3442 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3443 badge_ = [badge_ retain];
3444
3445 #ifdef USE_BADGES
3446 if (NSString *mode = [package mode]) {
3447 [badge_ setImage:[UIImage applicationImageNamed:
3448 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3449 ]];
3450
3451 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
3452 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
3453 } else if ([package half]) {
3454 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3455 [status_ setText:@"Package Damaged"];
3456 [status_ setColor:[UIColor redColor]];
3457 } else {
3458 [badge_ setImage:nil];
3459 [status_ setText:nil];
3460 }
3461 #endif
3462 }
3463
3464 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3465 if (icon_ != nil) {
3466 CGRect rect;
3467 rect.size = [icon_ size];
3468
3469 rect.size.width /= 2;
3470 rect.size.height /= 2;
3471
3472 rect.origin.x = 25 - rect.size.width / 2;
3473 rect.origin.y = 25 - rect.size.height / 2;
3474
3475 [icon_ drawInRect:rect];
3476 }
3477
3478 if (badge_ != nil) {
3479 CGSize size = [badge_ size];
3480
3481 [badge_ drawAtPoint:CGPointMake(
3482 36 - size.width / 2,
3483 36 - size.height / 2
3484 )];
3485 }
3486
3487 if (selected)
3488 UISetColor(White_);
3489
3490 if (!selected)
3491 UISetColor(Black_);
3492 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3493 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3494
3495 if (!selected)
3496 UISetColor(Gray_);
3497 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3498
3499 [super drawContentInRect:rect selected:selected];
3500 }
3501
3502 + (int) heightForPackage:(Package *)package {
3503 NSString *tagline([package tagline]);
3504 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
3505 #ifdef USE_BADGES
3506 if ([package hasMode] || [package half])
3507 return height + 96;
3508 else
3509 #endif
3510 return height + 73;
3511 }
3512
3513 @end
3514 /* }}} */
3515 /* Section Cell {{{ */
3516 @interface SectionCell : UISimpleTableCell {
3517 NSString *section_;
3518 NSString *name_;
3519 NSString *count_;
3520 UIImage *icon_;
3521 _UISwitchSlider *switch_;
3522 BOOL editing_;
3523 }
3524
3525 - (id) init;
3526 - (void) setSection:(Section *)section editing:(BOOL)editing;
3527
3528 @end
3529
3530 @implementation SectionCell
3531
3532 - (void) clearSection {
3533 if (section_ != nil) {
3534 [section_ release];
3535 section_ = nil;
3536 }
3537
3538 if (name_ != nil) {
3539 [name_ release];
3540 name_ = nil;
3541 }
3542
3543 if (count_ != nil) {
3544 [count_ release];
3545 count_ = nil;
3546 }
3547 }
3548
3549 - (void) dealloc {
3550 [self clearSection];
3551 [icon_ release];
3552 [switch_ release];
3553 [super dealloc];
3554 }
3555
3556 - (id) init {
3557 if ((self = [super init]) != nil) {
3558 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
3559
3560 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
3561 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
3562 } return self;
3563 }
3564
3565 - (void) onSwitch:(id)sender {
3566 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3567 if (metadata == nil) {
3568 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3569 [Sections_ setObject:metadata forKey:section_];
3570 }
3571
3572 Changed_ = true;
3573 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3574 }
3575
3576 - (void) setSection:(Section *)section editing:(BOOL)editing {
3577 if (editing != editing_) {
3578 if (editing_)
3579 [switch_ removeFromSuperview];
3580 else
3581 [self addSubview:switch_];
3582 editing_ = editing;
3583 }
3584
3585 [self clearSection];
3586
3587 if (section == nil) {
3588 name_ = [@"All Packages" retain];
3589 count_ = nil;
3590 } else {
3591 section_ = [section name];
3592 if (section_ != nil)
3593 section_ = [section_ retain];
3594 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3595 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
3596
3597 if (editing_)
3598 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
3599 }
3600 }
3601
3602 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3603 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3604
3605 if (selected)
3606 UISetColor(White_);
3607
3608 if (!selected)
3609 UISetColor(Black_);
3610 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
3611
3612 CGSize size = [count_ sizeWithFont:Font14_];
3613
3614 UISetColor(White_);
3615 if (count_ != nil)
3616 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
3617
3618 [super drawContentInRect:rect selected:selected];
3619 }
3620
3621 @end
3622 /* }}} */
3623
3624 /* File Table {{{ */
3625 @interface FileTable : RVPage {
3626 _transient Database *database_;
3627 Package *package_;
3628 NSString *name_;
3629 NSMutableArray *files_;
3630 UITable *list_;
3631 }
3632
3633 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3634 - (void) setPackage:(Package *)package;
3635
3636 @end
3637
3638 @implementation FileTable
3639
3640 - (void) dealloc {
3641 if (package_ != nil)
3642 [package_ release];
3643 if (name_ != nil)
3644 [name_ release];
3645 [files_ release];
3646 [list_ release];
3647 [super dealloc];
3648 }
3649
3650 - (int) numberOfRowsInTable:(UITable *)table {
3651 return files_ == nil ? 0 : [files_ count];
3652 }
3653
3654 - (float) table:(UITable *)table heightForRow:(int)row {
3655 return 24;
3656 }
3657
3658 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3659 if (reusing == nil) {
3660 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3661 UIFont *font = [UIFont systemFontOfSize:16];
3662 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3663 }
3664 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3665 return reusing;
3666 }
3667
3668 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3669 return NO;
3670 }
3671
3672 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3673 if ((self = [super initWithBook:book]) != nil) {
3674 database_ = database;
3675
3676 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3677
3678 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3679 [self addSubview:list_];
3680
3681 UITableColumn *column = [[[UITableColumn alloc]
3682 initWithTitle:@"Name"
3683 identifier:@"name"
3684 width:[self frame].size.width
3685 ] autorelease];
3686
3687 [list_ setDataSource:self];
3688 [list_ setSeparatorStyle:1];
3689 [list_ addTableColumn:column];
3690 [list_ setDelegate:self];
3691 [list_ setReusesTableCells:YES];
3692 } return self;
3693 }
3694
3695 - (void) setPackage:(Package *)package {
3696 if (package_ != nil) {
3697 [package_ autorelease];
3698 package_ = nil;
3699 }
3700
3701 if (name_ != nil) {
3702 [name_ release];
3703 name_ = nil;
3704 }
3705
3706 [files_ removeAllObjects];
3707
3708 if (package != nil) {
3709 package_ = [package retain];
3710 name_ = [[package id] retain];
3711
3712 if (NSArray *files = [package files])
3713 [files_ addObjectsFromArray:files];
3714
3715 if ([files_ count] != 0) {
3716 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3717 [files_ removeObjectAtIndex:0];
3718 [files_ sortUsingSelector:@selector(compareByPath:)];
3719
3720 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3721 [stack addObject:@"/"];
3722
3723 for (int i(0), e([files_ count]); i != e; ++i) {
3724 NSString *file = [files_ objectAtIndex:i];
3725 while (![file hasPrefix:[stack lastObject]])
3726 [stack removeLastObject];
3727 NSString *directory = [stack lastObject];
3728 [stack addObject:[file stringByAppendingString:@"/"]];
3729 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3730 ([stack count] - 2) * 3, "",
3731 [file substringFromIndex:[directory length]]
3732 ]];
3733 }
3734 }
3735 }
3736
3737 [list_ reloadData];
3738 }
3739
3740 - (void) resetViewAnimated:(BOOL)animated {
3741 [list_ resetViewAnimated:animated];
3742 }
3743
3744 - (void) reloadData {
3745 [self setPackage:[database_ packageWithName:name_]];
3746 [self reloadButtons];
3747 }
3748
3749 - (NSString *) title {
3750 return @"Installed Files";
3751 }
3752
3753 - (NSString *) backButtonTitle {
3754 return @"Files";
3755 }
3756
3757 @end
3758 /* }}} */
3759 /* Package View {{{ */
3760 @interface PackageView : BrowserView {
3761 _transient Database *database_;
3762 Package *package_;
3763 NSString *name_;
3764 NSMutableArray *buttons_;
3765 }
3766
3767 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3768 - (void) setPackage:(Package *)package;
3769
3770 @end
3771
3772 @implementation PackageView
3773
3774 - (void) dealloc {
3775 if (package_ != nil)
3776 [package_ release];
3777 if (name_ != nil)
3778 [name_ release];
3779 [buttons_ release];
3780 [super dealloc];
3781 }
3782
3783 - (void) _clickButtonWithName:(NSString *)name {
3784 if ([name isEqualToString:@"Install"])
3785 [delegate_ installPackage:package_];
3786 else if ([name isEqualToString:@"Reinstall"])
3787 [delegate_ installPackage:package_];
3788 else if ([name isEqualToString:@"Remove"])
3789 [delegate_ removePackage:package_];
3790 else if ([name isEqualToString:@"Upgrade"])
3791 [delegate_ installPackage:package_];
3792 else _assert(false);
3793 }
3794
3795 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
3796 int count = [buttons_ count];
3797 _assert(count != 0);
3798 _assert(button <= count + 1);
3799
3800 if (count != button - 1)
3801 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3802
3803 [sheet dismiss];
3804 }
3805
3806 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3807 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3808 return [super webView:sender didFinishLoadForFrame:frame];
3809 }
3810
3811 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3812 [window setValue:package_ forKey:@"package"];
3813 [super webView:sender didClearWindowObject:window forFrame:frame];
3814 }
3815
3816 #if !AlwaysReload
3817 - (void) _rightButtonClicked {
3818 /*[super _rightButtonClicked];
3819 return;*/
3820
3821 int count = [buttons_ count];
3822 _assert(count != 0);
3823
3824 if (count == 1)
3825 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3826 else {
3827 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3828 [buttons addObjectsFromArray:buttons_];
3829 [buttons addObject:@"Cancel"];
3830
3831 [delegate_ slideUp:[[[UIActionSheet alloc]
3832 initWithTitle:nil
3833 buttons:buttons
3834 defaultButtonIndex:2
3835 delegate:self
3836 context:@"manage"
3837 ] autorelease]];
3838 }
3839 }
3840 #endif
3841
3842 - (NSString *) _rightButtonTitle {
3843 int count = [buttons_ count];
3844 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3845 }
3846
3847 - (NSString *) backButtonTitle {
3848 return @"Details";
3849 }
3850
3851 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3852 if ((self = [super initWithBook:book]) != nil) {
3853 database_ = database;
3854 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3855 } return self;
3856 }
3857
3858 - (void) setPackage:(Package *)package {
3859 if (package_ != nil) {
3860 [package_ autorelease];
3861 package_ = nil;
3862 }
3863
3864 if (name_ != nil) {
3865 [name_ release];
3866 name_ = nil;
3867 }
3868
3869 [buttons_ removeAllObjects];
3870
3871 if (package != nil) {
3872 package_ = [package retain];
3873 name_ = [[package id] retain];
3874
3875 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
3876
3877 if ([package_ source] == nil);
3878 else if ([package_ upgradableAndEssential:NO])
3879 [buttons_ addObject:@"Upgrade"];
3880 else if ([package_ installed] == nil)
3881 [buttons_ addObject:@"Install"];
3882 else
3883 [buttons_ addObject:@"Reinstall"];
3884 if ([package_ installed] != nil)
3885 [buttons_ addObject:@"Remove"];
3886 }
3887 }
3888
3889 - (void) reloadData {
3890 [self setPackage:[database_ packageWithName:name_]];
3891 [self reloadButtons];
3892 }
3893
3894 @end
3895 /* }}} */
3896 /* Package Table {{{ */
3897 @interface PackageTable : RVPage {
3898 _transient Database *database_;
3899 NSString *title_;
3900 SEL filter_;
3901 id object_;
3902 NSMutableArray *packages_;
3903 NSMutableArray *sections_;
3904 UISectionList *list_;
3905 }
3906
3907 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3908
3909 - (void) setDelegate:(id)delegate;
3910 - (void) setObject:(id)object;
3911
3912 - (void) reloadData;
3913 - (void) resetCursor;
3914
3915 - (UISectionList *) list;
3916
3917 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3918
3919 @end
3920
3921 @implementation PackageTable
3922
3923 - (void) dealloc {
3924 [list_ setDataSource:nil];
3925
3926 [title_ release];
3927 if (object_ != nil)
3928 [object_ release];
3929 [packages_ release];
3930 [sections_ release];
3931 [list_ release];
3932 [super dealloc];
3933 }
3934
3935 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3936 return [sections_ count];
3937 }
3938
3939 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3940 return [[sections_ objectAtIndex:section] name];
3941 }
3942
3943 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3944 return [[sections_ objectAtIndex:section] row];
3945 }
3946
3947 - (int) numberOfRowsInTable:(UITable *)table {
3948 return [packages_ count];
3949 }
3950
3951 - (float) table:(UITable *)table heightForRow:(int)row {
3952 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3953 }
3954
3955 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3956 if (reusing == nil)
3957 reusing = [[[PackageCell alloc] init] autorelease];
3958 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3959 return reusing;
3960 }
3961
3962 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3963 return NO;
3964 }
3965
3966 - (void) tableRowSelected:(NSNotification *)notification {
3967 int row = [[notification object] selectedRow];
3968 if (row == INT_MAX)
3969 return;
3970
3971 Package *package = [packages_ objectAtIndex:row];
3972 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3973 [view setDelegate:delegate_];
3974 [view setPackage:package];
3975 [book_ pushPage:view];
3976 }
3977
3978 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3979 if ((self = [super initWithBook:book]) != nil) {
3980 database_ = database;
3981 title_ = [title retain];
3982 filter_ = filter;
3983 object_ = object == nil ? nil : [object retain];
3984
3985 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3986 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3987
3988 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3989 [list_ setDataSource:self];
3990
3991 UITableColumn *column = [[[UITableColumn alloc]
3992 initWithTitle:@"Name"
3993 identifier:@"name"
3994 width:[self frame].size.width
3995 ] autorelease];
3996
3997 UITable *table = [list_ table];
3998 [table setSeparatorStyle:1];
3999 [table addTableColumn:column];
4000 [table setDelegate:self];
4001 [table setReusesTableCells:YES];
4002
4003 [self addSubview:list_];
4004 [self reloadData];
4005
4006 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4007 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4008 } return self;
4009 }
4010
4011 - (void) setDelegate:(id)delegate {
4012 delegate_ = delegate;
4013 }
4014
4015 - (void) setObject:(id)object {
4016 if (object_ != nil)
4017 [object_ release];
4018 if (object == nil)
4019 object_ = nil;
4020 else
4021 object_ = [object retain];
4022 }
4023
4024 - (void) reloadData {
4025 NSArray *packages = [database_ packages];
4026
4027 [packages_ removeAllObjects];
4028 [sections_ removeAllObjects];
4029
4030 for (size_t i(0); i != [packages count]; ++i) {
4031 Package *package([packages objectAtIndex:i]);
4032 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
4033 [packages_ addObject:package];
4034 }
4035
4036 Section *section = nil;
4037
4038 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4039 Package *package = [packages_ objectAtIndex:offset];
4040 NSString *name = [package index];
4041
4042 if (section == nil || ![[section name] isEqualToString:name]) {
4043 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4044 [sections_ addObject:section];
4045 }
4046
4047 [section addToCount];
4048 }
4049
4050 [list_ reloadData];
4051 }
4052
4053 - (NSString *) title {
4054 return title_;
4055 }
4056
4057 - (void) resetViewAnimated:(BOOL)animated {
4058 [list_ resetViewAnimated:animated];
4059 }
4060
4061 - (void) resetCursor {
4062 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4063 }
4064
4065 - (UISectionList *) list {
4066 return list_;
4067 }
4068
4069 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4070 [list_ setShouldHideHeaderInShortLists:hide];
4071 }
4072
4073 @end
4074 /* }}} */
4075
4076 /* Add Source View {{{ */
4077 @interface AddSourceView : RVPage {
4078 _transient Database *database_;
4079 }
4080
4081 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4082
4083 @end
4084
4085 @implementation AddSourceView
4086
4087 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4088 if ((self = [super initWithBook:book]) != nil) {
4089 database_ = database;
4090 } return self;
4091 }
4092
4093 @end
4094 /* }}} */
4095 /* Source Cell {{{ */
4096 @interface SourceCell : UITableCell {
4097 UIImage *icon_;
4098 NSString *origin_;
4099 NSString *description_;
4100 NSString *label_;
4101 }
4102
4103 - (void) dealloc;
4104
4105 - (SourceCell *) initWithSource:(Source *)source;
4106
4107 @end
4108
4109 @implementation SourceCell
4110
4111 - (void) dealloc {
4112 [icon_ release];
4113 [origin_ release];
4114 [description_ release];
4115 [label_ release];
4116 [super dealloc];
4117 }
4118
4119 - (SourceCell *) initWithSource:(Source *)source {
4120 if ((self = [super init]) != nil) {
4121 if (icon_ == nil)
4122 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4123 if (icon_ == nil)
4124 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4125 icon_ = [icon_ retain];
4126
4127 origin_ = [[source name] retain];
4128 label_ = [[source uri] retain];
4129 description_ = [[source description] retain];
4130 } return self;
4131 }
4132
4133 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4134 if (icon_ != nil)
4135 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4136
4137 if (selected)
4138 UISetColor(White_);
4139
4140 if (!selected)
4141 UISetColor(Black_);
4142 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4143
4144 if (!selected)
4145 UISetColor(Blue_);
4146 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4147
4148 if (!selected)
4149 UISetColor(Gray_);
4150 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4151
4152 [super drawContentInRect:rect selected:selected];
4153 }
4154
4155 @end
4156 /* }}} */
4157 /* Source Table {{{ */
4158 @interface SourceTable : RVPage {
4159 _transient Database *database_;
4160 UISectionList *list_;
4161 NSMutableArray *sources_;
4162 UIActionSheet *alert_;
4163 int offset_;
4164
4165 NSString *href_;
4166 UIProgressHUD *hud_;
4167 NSError *error_;
4168
4169 //NSURLConnection *installer_;
4170 NSURLConnection *trivial_bz2_;
4171 NSURLConnection *trivial_gz_;
4172 //NSURLConnection *automatic_;
4173
4174 BOOL trivial_;
4175 }
4176
4177 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4178
4179 @end
4180
4181 @implementation SourceTable
4182
4183 - (void) _deallocConnection:(NSURLConnection *)connection {
4184 if (connection != nil) {
4185 [connection cancel];
4186 //[connection setDelegate:nil];
4187 [connection release];
4188 }
4189 }
4190
4191 - (void) dealloc {
4192 [[list_ table] setDelegate:nil];
4193 [list_ setDataSource:nil];
4194
4195 if (href_ != nil)
4196 [href_ release];
4197 if (hud_ != nil)
4198 [hud_ release];
4199 if (error_ != nil)
4200 [error_ release];
4201
4202 //[self _deallocConnection:installer_];
4203 [self _deallocConnection:trivial_gz_];
4204 [self _deallocConnection:trivial_bz2_];
4205 //[self _deallocConnection:automatic_];
4206
4207 [sources_ release];
4208 [list_ release];
4209 [super dealloc];
4210 }
4211
4212 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4213 return offset_ == 0 ? 1 : 2;
4214 }
4215
4216 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4217 switch (section + (offset_ == 0 ? 1 : 0)) {
4218 case 0: return @"Entered by User";
4219 case 1: return @"Installed by Packages";
4220
4221 default:
4222 _assert(false);
4223 return nil;
4224 }
4225 }
4226
4227 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4228 switch (section + (offset_ == 0 ? 1 : 0)) {
4229 case 0: return 0;
4230 case 1: return offset_;
4231
4232 default:
4233 _assert(false);
4234 return -1;
4235 }
4236 }
4237
4238 - (int) numberOfRowsInTable:(UITable *)table {
4239 return [sources_ count];
4240 }
4241
4242 - (float) table:(UITable *)table heightForRow:(int)row {
4243 Source *source = [sources_ objectAtIndex:row];
4244 return [source description] == nil ? 56 : 73;
4245 }
4246
4247 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4248 Source *source = [sources_ objectAtIndex:row];
4249 // XXX: weird warning, stupid selectors ;P
4250 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4251 }
4252
4253 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4254 return YES;
4255 }
4256
4257 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4258 return YES;
4259 }
4260
4261 - (void) tableRowSelected:(NSNotification*)notification {
4262 UITable *table([list_ table]);
4263 int row([table selectedRow]);
4264 if (row == INT_MAX)
4265 return;
4266
4267 Source *source = [sources_ objectAtIndex:row];
4268
4269 PackageTable *packages = [[[PackageTable alloc]
4270 initWithBook:book_
4271 database:database_
4272 title:[source label]
4273 filter:@selector(isVisibleInSource:)
4274 with:source
4275 ] autorelease];
4276
4277 [packages setDelegate:delegate_];
4278
4279 [book_ pushPage:packages];
4280 }
4281
4282 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4283 Source *source = [sources_ objectAtIndex:row];
4284 return [source record] != nil;
4285 }
4286
4287 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4288 [[list_ table] setDeleteConfirmationRow:row];
4289 }
4290
4291 - (void) table:(UITable *)table deleteRow:(int)row {
4292 Source *source = [sources_ objectAtIndex:row];
4293 [Sources_ removeObjectForKey:[source key]];
4294 [delegate_ syncData];
4295 }
4296
4297 - (void) _endConnection:(NSURLConnection *)connection {
4298 NSURLConnection **field = NULL;
4299 if (connection == trivial_bz2_)
4300 field = &trivial_bz2_;
4301 else if (connection == trivial_gz_)
4302 field = &trivial_gz_;
4303 _assert(field != NULL);
4304 [connection release];
4305 *field = nil;
4306
4307 if (
4308 trivial_bz2_ == nil &&
4309 trivial_gz_ == nil
4310 ) {
4311 [delegate_ setStatusBarShowsProgress:NO];
4312
4313 [hud_ show:NO];
4314 [hud_ removeFromSuperview];
4315 [hud_ autorelease];
4316 hud_ = nil;
4317
4318 if (trivial_) {
4319 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4320 @"deb", @"Type",
4321 href_, @"URI",
4322 @"./", @"Distribution",
4323 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4324
4325 [delegate_ syncData];
4326 } else if (error_ != nil) {
4327 UIActionSheet *sheet = [[[UIActionSheet alloc]
4328 initWithTitle:@"Verification Error"
4329 buttons:[NSArray arrayWithObjects:@"OK", nil]
4330 defaultButtonIndex:0
4331 delegate:self
4332 context:@"urlerror"
4333 ] autorelease];
4334
4335 [sheet setBodyText:[error_ localizedDescription]];
4336 [sheet popupAlertAnimated:YES];
4337 } else {
4338 UIActionSheet *sheet = [[[UIActionSheet alloc]
4339 initWithTitle:@"Did not Find Repository"
4340 buttons:[NSArray arrayWithObjects:@"OK", nil]
4341 defaultButtonIndex:0
4342 delegate:self
4343 context:@"trivial"
4344 ] autorelease];
4345
4346 [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."];
4347 [sheet popupAlertAnimated:YES];
4348 }
4349
4350 [href_ release];
4351 href_ = nil;
4352
4353 if (error_ != nil) {
4354 [error_ release];
4355 error_ = nil;
4356 }
4357 }
4358 }
4359
4360 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4361 switch ([response statusCode]) {
4362 case 200:
4363 trivial_ = YES;
4364 }
4365 }
4366
4367 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4368 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4369 if (error_ != nil)
4370 error_ = [error retain];
4371 [self _endConnection:connection];
4372 }
4373
4374 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4375 [self _endConnection:connection];
4376 }
4377
4378 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4379 NSMutableURLRequest *request = [NSMutableURLRequest
4380 requestWithURL:[NSURL URLWithString:href]
4381 cachePolicy:NSURLRequestUseProtocolCachePolicy
4382 timeoutInterval:20.0
4383 ];
4384
4385 [request setHTTPMethod:method];
4386
4387 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4388 }
4389
4390 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4391 NSString *context = [sheet context];
4392 if ([context isEqualToString:@"source"])
4393 switch (button) {
4394 case 1: {
4395 NSString *href = [[sheet textField] text];
4396
4397 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4398
4399 if (![href hasSuffix:@"/"])
4400 href_ = [href stringByAppendingString:@"/"];
4401 else
4402 href_ = href;
4403 href_ = [href_ retain];
4404
4405 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4406 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4407 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4408
4409 trivial_ = false;
4410
4411 hud_ = [delegate_ addProgressHUD];
4412 [hud_ setText:@"Verifying URL"];
4413 } break;
4414
4415 case 2:
4416 break;
4417
4418 default:
4419 _assert(false);
4420 }
4421
4422 [sheet dismiss];
4423 }
4424
4425 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4426 if ((self = [super initWithBook:book]) != nil) {
4427 database_ = database;
4428 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4429
4430 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4431 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4432 [list_ setShouldHideHeaderInShortLists:NO];
4433
4434 [self addSubview:list_];
4435 [list_ setDataSource:self];
4436
4437 UITableColumn *column = [[UITableColumn alloc]
4438 initWithTitle:@"Name"
4439 identifier:@"name"
4440 width:[self frame].size.width
4441 ];
4442
4443 UITable *table = [list_ table];
4444 [table setSeparatorStyle:1];
4445 [table addTableColumn:column];
4446 [table setDelegate:self];
4447
4448 [self reloadData];
4449
4450 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4451 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4452 } return self;
4453 }
4454
4455 - (void) reloadData {
4456 pkgSourceList list;
4457 _assert(list.ReadMainList());
4458
4459 [sources_ removeAllObjects];
4460 [sources_ addObjectsFromArray:[database_ sources]];
4461 _trace();
4462 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4463 _trace();
4464
4465 int count = [sources_ count];
4466 for (offset_ = 0; offset_ != count; ++offset_) {
4467 Source *source = [sources_ objectAtIndex:offset_];
4468 if ([source record] == nil)
4469 break;
4470 }
4471
4472 [list_ reloadData];
4473 }
4474
4475 - (void) resetViewAnimated:(BOOL)animated {
4476 [list_ resetViewAnimated:animated];
4477 }
4478
4479 - (void) _leftButtonClicked {
4480 /*[book_ pushPage:[[[AddSourceView alloc]
4481 initWithBook:book_
4482 database:database_
4483 ] autorelease]];*/
4484
4485 UIActionSheet *sheet = [[[UIActionSheet alloc]
4486 initWithTitle:@"Enter Cydia/APT URL"
4487 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4488 defaultButtonIndex:0
4489 delegate:self
4490 context:@"source"
4491 ] autorelease];
4492
4493 [sheet addTextFieldWithValue:@"http://" label:@""];
4494
4495 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4496 [traits setAutocapitalizationType:0];
4497 [traits setKeyboardType:3];
4498 [traits setAutocorrectionType:1];
4499
4500 [sheet popupAlertAnimated:YES];
4501 }
4502
4503 - (void) _rightButtonClicked {
4504 UITable *table = [list_ table];
4505 BOOL editing = [table isRowDeletionEnabled];
4506 [table enableRowDeletion:!editing animated:YES];
4507 [book_ reloadButtonsForPage:self];
4508 }
4509
4510 - (NSString *) title {
4511 return @"Sources";
4512 }
4513
4514 - (NSString *) leftButtonTitle {
4515 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4516 }
4517
4518 - (NSString *) rightButtonTitle {
4519 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4520 }
4521
4522 - (UINavigationButtonStyle) rightButtonStyle {
4523 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4524 }
4525
4526 @end
4527 /* }}} */
4528
4529 /* Installed View {{{ */
4530 @interface InstalledView : RVPage {
4531 _transient Database *database_;
4532 PackageTable *packages_;
4533 BOOL expert_;
4534 }
4535
4536 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4537
4538 @end
4539
4540 @implementation InstalledView
4541
4542 - (void) dealloc {
4543 [packages_ release];
4544 [super dealloc];
4545 }
4546
4547 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4548 if ((self = [super initWithBook:book]) != nil) {
4549 database_ = database;
4550
4551 packages_ = [[PackageTable alloc]
4552 initWithBook:book
4553 database:database
4554 title:nil
4555 filter:@selector(isInstalledAndVisible:)
4556 with:[NSNumber numberWithBool:YES]
4557 ];
4558
4559 [self addSubview:packages_];
4560
4561 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4562 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4563 } return self;
4564 }
4565
4566 - (void) resetViewAnimated:(BOOL)animated {
4567 [packages_ resetViewAnimated:animated];
4568 }
4569
4570 - (void) reloadData {
4571 [packages_ reloadData];
4572 }
4573
4574 - (void) _rightButtonClicked {
4575 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4576 [packages_ reloadData];
4577 expert_ = !expert_;
4578 [book_ reloadButtonsForPage:self];
4579 }
4580
4581 - (NSString *) title {
4582 return @"Installed";
4583 }
4584
4585 - (NSString *) backButtonTitle {
4586 return @"Packages";
4587 }
4588
4589 - (NSString *) rightButtonTitle {
4590 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4591 }
4592
4593 - (UINavigationButtonStyle) rightButtonStyle {
4594 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
4595 }
4596
4597 - (void) setDelegate:(id)delegate {
4598 [super setDelegate:delegate];
4599 [packages_ setDelegate:delegate];
4600 }
4601
4602 @end
4603 /* }}} */
4604
4605 /* Home View {{{ */
4606 @interface HomeView : BrowserView {
4607 }
4608
4609 @end
4610
4611 @implementation HomeView
4612
4613 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4614 [sheet dismiss];
4615 }
4616
4617 - (void) _leftButtonClicked {
4618 UIActionSheet *sheet = [[[UIActionSheet alloc]
4619 initWithTitle:@"About Cydia Installer"
4620 buttons:[NSArray arrayWithObjects:@"Close", nil]
4621 defaultButtonIndex:0
4622 delegate:self
4623 context:@"about"
4624 ] autorelease];
4625
4626 [sheet setBodyText:
4627 @"Copyright (C) 2008\n"
4628 "Jay Freeman (saurik)\n"
4629 "saurik@saurik.com\n"
4630 "http://www.saurik.com/\n"
4631 "\n"
4632 "The Okori Group\n"
4633 "http://www.theokorigroup.com/\n"
4634 "\n"
4635 "College of Creative Studies,\n"
4636 "University of California,\n"
4637 "Santa Barbara\n"
4638 "http://www.ccs.ucsb.edu/"
4639 ];
4640
4641 [sheet popupAlertAnimated:YES];
4642 }
4643
4644 - (NSString *) leftButtonTitle {
4645 return @"About";
4646 }
4647
4648 @end
4649 /* }}} */
4650 /* Manage View {{{ */
4651 @interface ManageView : BrowserView {
4652 }
4653
4654 @end
4655
4656 @implementation ManageView
4657
4658 - (NSString *) title {
4659 return @"Manage";
4660 }
4661
4662 - (void) _leftButtonClicked {
4663 [delegate_ askForSettings];
4664 }
4665
4666 - (NSString *) leftButtonTitle {
4667 return @"Settings";
4668 }
4669
4670 - (NSString *) _rightButtonTitle {
4671 return nil;
4672 }
4673
4674 @end
4675 /* }}} */
4676
4677 @interface WebView (Cydia)
4678 - (void) setScriptDebugDelegate:(id)delegate;
4679 - (void) _setFormDelegate:(id)delegate;
4680 - (void) _setUIKitDelegate:(id)delegate;
4681 - (void) setWebMailDelegate:(id)delegate;
4682 - (void) _setLayoutInterval:(float)interval;
4683 @end
4684
4685 /* Indirect Delegate {{{ */
4686 @interface IndirectDelegate : NSProxy {
4687 _transient volatile id delegate_;
4688 }
4689
4690 - (void) setDelegate:(id)delegate;
4691 - (id) initWithDelegate:(id)delegate;
4692 @end
4693
4694 @implementation IndirectDelegate
4695
4696 - (void) setDelegate:(id)delegate {
4697 delegate_ = delegate;
4698 }
4699
4700 - (id) initWithDelegate:(id)delegate {
4701 delegate_ = delegate;
4702 return self;
4703 }
4704
4705 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4706 if (delegate_ != nil)
4707 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4708 return sig;
4709 // XXX: I fucking hate Apple so very very bad
4710 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4711 }
4712
4713 - (void) forwardInvocation:(NSInvocation *)inv {
4714 SEL sel = [inv selector];
4715 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4716 [inv invokeWithTarget:delegate_];
4717 }
4718
4719 @end
4720 /* }}} */
4721 /* Browser Implementation {{{ */
4722 @implementation BrowserView
4723
4724 - (void) dealloc {
4725 WebView *webview = [webview_ webView];
4726 [webview setFrameLoadDelegate:nil];
4727 [webview setResourceLoadDelegate:nil];
4728 [webview setUIDelegate:nil];
4729 [webview setScriptDebugDelegate:nil];
4730 [webview setPolicyDelegate:nil];
4731
4732 [webview setDownloadDelegate:nil];
4733
4734 [webview _setFormDelegate:nil];
4735 [webview _setUIKitDelegate:nil];
4736 [webview setWebMailDelegate:nil];
4737 [webview setEditingDelegate:nil];
4738
4739 [webview_ setDelegate:nil];
4740 [webview_ setGestureDelegate:nil];
4741
4742 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4743
4744 [webview close];
4745
4746 #if RecycleWebViews
4747 [webview_ removeFromSuperview];
4748 [Documents_ addObject:[webview_ autorelease]];
4749 #else
4750 [webview_ release];
4751 #endif
4752
4753 [indirect_ setDelegate:nil];
4754 [indirect_ release];
4755
4756 [scroller_ setDelegate:nil];
4757
4758 [scroller_ release];
4759 [urls_ release];
4760 [indicator_ release];
4761 if (title_ != nil)
4762 [title_ release];
4763 [super dealloc];
4764 }
4765
4766 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4767 [self loadRequest:[NSURLRequest
4768 requestWithURL:url
4769 cachePolicy:policy
4770 timeoutInterval:30.0
4771 ]];
4772 }
4773
4774 - (void) loadURL:(NSURL *)url {
4775 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4776 }
4777
4778 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4779 NSMutableURLRequest *copy = [request mutableCopy];
4780
4781 if (Machine_ != NULL)
4782 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4783 if (UniqueID_ != nil)
4784 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
4785
4786 if (Role_ != nil)
4787 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4788
4789 return copy;
4790 }
4791
4792 - (void) loadRequest:(NSURLRequest *)request {
4793 pushed_ = true;
4794 [webview_ loadRequest:request];
4795 }
4796
4797 - (void) reloadURL {
4798 if ([urls_ count] == 0)
4799 return;
4800 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4801 [urls_ removeLastObject];
4802 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4803 }
4804
4805 - (WebView *) webView {
4806 return [webview_ webView];
4807 }
4808
4809 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4810 [scroller_ setContentSize:frame.size];
4811 }
4812
4813 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4814 [self view:sender didSetFrame:frame];
4815 }
4816
4817 - (void) pushPage:(RVPage *)page {
4818 [self setBackButtonTitle:title_];
4819 [page setDelegate:delegate_];
4820 [book_ pushPage:page];
4821 }
4822
4823 - (BOOL) getSpecial:(NSURL *)url {
4824 NSString *href([url absoluteString]);
4825 NSString *scheme([[url scheme] lowercaseString]);
4826
4827 RVPage *page = nil;
4828
4829 if ([href hasPrefix:@"apptapp://package/"])
4830 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
4831 else if ([scheme isEqualToString:@"cydia"]) {
4832 page = [delegate_ pageForURL:url hasTag:NULL];
4833 if (page == nil)
4834 return false;
4835 } else if (![scheme isEqualToString:@"apptapp"])
4836 return false;
4837
4838 if (page != nil)
4839 [self pushPage:page];
4840 return true;
4841 }
4842
4843 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4844 [window setValue:delegate_ forKey:@"cydia"];
4845 }
4846
4847 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4848 if (NSURL *url = [request URL]) {
4849 if (![self getSpecial:url]) {
4850 NSString *scheme([[url scheme] lowercaseString]);
4851 if ([scheme isEqualToString:@"mailto"])
4852 [delegate_ openMailToURL:url];
4853 else goto use;
4854 }
4855
4856 [listener ignore];
4857 } else use:
4858 [listener use];
4859 }
4860
4861 - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4862 if ([WebView canShowMIMEType:type])
4863 [listener use];
4864 else {
4865 // XXX: handle more mime types!
4866 [listener ignore];
4867 if (frame == [webView mainFrame])
4868 [UIApp openURL:[request URL]];
4869 }
4870 }
4871
4872 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4873 NSURL *url([request URL]);
4874
4875 if (url == nil) use: {
4876 [listener use];
4877 return;
4878 }
4879
4880 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4881
4882 if (
4883 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4884 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4885 ) {
4886 open:
4887 [UIApp openURL:url];
4888 ignore:
4889 [listener ignore];
4890 return;
4891 }
4892
4893 int store(_not(int));
4894 if (NSURL *itms = [url itmsURL:&store]) {
4895 NSLog(@"itms#%@#%u#%@", url, store, itms);
4896 if (
4897 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4898 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4899 ) {
4900 url = itms;
4901 goto open;
4902 }
4903 }
4904
4905 NSString *scheme([[url scheme] lowercaseString]);
4906
4907 if ([scheme isEqualToString:@"tel"]) {
4908 // XXX: intelligence
4909 goto open;
4910 }
4911
4912 if ([scheme isEqualToString:@"mailto"]) {
4913 [delegate_ openMailToURL:url];
4914 goto ignore;
4915 }
4916
4917 if ([self getSpecial:url])
4918 goto ignore;
4919 else if ([WebView _canHandleRequest:request])
4920 goto use;
4921 else if ([url isSpringboardHandledURL])
4922 goto open;
4923 else
4924 goto use;
4925 }
4926
4927 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4928 //lprintf("Status:%s\n", [text UTF8String]);
4929 }
4930
4931 - (void) _pushPage {
4932 if (pushed_)
4933 return;
4934 pushed_ = true;
4935 [book_ pushPage:self];
4936 }
4937
4938 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4939 NSURL *url = [request URL];
4940 if ([self getSpecial:url])
4941 return nil;
4942 [self _pushPage];
4943 return [self _addHeadersToRequest:request];
4944 }
4945
4946 - (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
4947 [self setBackButtonTitle:title_];
4948
4949 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
4950 [browser setDelegate:delegate_];
4951
4952 if (pushed) {
4953 [browser loadRequest:[self _addHeadersToRequest:request]];
4954 [book_ pushPage:browser];
4955 }
4956
4957 return [browser webView];
4958 }
4959
4960 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4961 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4962 }
4963
4964 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4965 return [self _createWebViewWithRequest:request pushed:YES];
4966 }
4967
4968 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4969 if ([frame parentFrame] != nil)
4970 return;
4971
4972 title_ = [title retain];
4973 [book_ reloadTitleForPage:self];
4974 }
4975
4976 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4977 if ([frame parentFrame] != nil)
4978 return;
4979
4980 reloading_ = false;
4981 loading_ = true;
4982 [indicator_ startAnimation];
4983 [self reloadButtons];
4984
4985 if (title_ != nil) {
4986 [title_ release];
4987 title_ = nil;
4988 }
4989
4990 [book_ reloadTitleForPage:self];
4991
4992 WebView *webview = [webview_ webView];
4993 NSString *href = [webview mainFrameURL];
4994 [urls_ addObject:[NSURL URLWithString:href]];
4995
4996 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4997
4998 CGRect webrect = [scroller_ bounds];
4999 webrect.size.height = 0;
5000 [webview_ setFrame:webrect];
5001 }
5002
5003 - (void) _finishLoading {
5004 if (!reloading_) {
5005 loading_ = false;
5006 [indicator_ stopAnimation];
5007 [self reloadButtons];
5008 }
5009 }
5010
5011 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
5012 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5013 }
5014
5015 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5016 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5017 }
5018
5019 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5020 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5021 }
5022
5023 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5024 return [webview_ webView:sender didCommitLoadForFrame:frame];
5025 }
5026
5027 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5028 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5029 }
5030
5031 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
5032 if ([frame parentFrame] == nil)
5033 [self _finishLoading];
5034 return [webview_ webView:sender didFinishLoadForFrame:frame];
5035 }
5036
5037 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5038 if ([frame parentFrame] != nil)
5039 return;
5040 [self _finishLoading];
5041
5042 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
5043 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
5044 [[error localizedDescription] stringByAddingPercentEscapes]
5045 ]]];
5046 }
5047
5048 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5049 #if ForSaurik
5050 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5051 #endif
5052 }
5053
5054 - (id) initWithBook:(RVBook *)book {
5055 if ((self = [super initWithBook:book]) != nil) {
5056 loading_ = false;
5057
5058 struct CGRect bounds = [self bounds];
5059
5060 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5061 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5062 [self addSubview:pinstripe];
5063
5064 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5065 [self addSubview:scroller_];
5066
5067 [scroller_ setScrollingEnabled:YES];
5068 [scroller_ setAdjustForContentSizeChange:YES];
5069 [scroller_ setClipsSubviews:YES];
5070 [scroller_ setAllowsRubberBanding:YES];
5071 [scroller_ setScrollDecelerationFactor:0.99];
5072 [scroller_ setDelegate:self];
5073
5074 CGRect webrect = [scroller_ bounds];
5075 webrect.size.height = 0;
5076
5077 WebView *webview;
5078
5079 #if RecycleWebViews
5080 webview_ = [Documents_ lastObject];
5081 if (webview_ != nil) {
5082 webview_ = [webview_ retain];
5083 webview = [webview_ webView];
5084 [Documents_ removeLastObject];
5085 [webview_ setFrame:webrect];
5086 } else {
5087 #else
5088 if (true) {
5089 #endif
5090 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
5091 webview = [webview_ webView];
5092
5093 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
5094
5095 [webview_ setAllowsMessaging:YES];
5096
5097 [webview_ setTilingEnabled:YES];
5098 [webview_ setDrawsGrid:NO];
5099 [webview_ setLogsTilingChanges:NO];
5100 [webview_ setTileMinificationFilter:kCAFilterNearest];
5101 [webview_ setDetectsPhoneNumbers:NO];
5102 [webview_ setAutoresizes:YES];
5103
5104 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5105 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5106 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
5107
5108 [webview_ _setDocumentType:0x4];
5109
5110 [webview_ setZoomsFocusedFormControl:YES];
5111 [webview_ setContentsPosition:7];
5112 [webview_ setEnabledGestures:0xa];
5113 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5114 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
5115
5116 [webview_ setSmoothsFonts:YES];
5117
5118 [webview _setUsesLoaderCache:YES];
5119 [webview setGroupName:@"Cydia"];
5120 //[webview _setLayoutInterval:0.5];
5121 }
5122
5123 [webview_ setDelegate:self];
5124 [webview_ setGestureDelegate:self];
5125 [scroller_ addSubview:webview_];
5126
5127 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5128
5129 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
5130 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
5131 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
5132
5133 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
5134 NSString *application = package == nil ? @"Cydia" : [NSString
5135 stringWithFormat:@"Cydia/%@",
5136 [package installed]
5137 ]; [webview setApplicationNameForUserAgent:application];
5138
5139 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5140
5141 [webview setFrameLoadDelegate:self];
5142 [webview setResourceLoadDelegate:indirect_];
5143 [webview setUIDelegate:self];
5144 [webview setScriptDebugDelegate:self];
5145 [webview setPolicyDelegate:self];
5146
5147 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
5148
5149 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5150 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5151 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5152 } return self;
5153 }
5154
5155 - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5156 [webview_ redrawScaledDocument];
5157 }
5158
5159 - (void) _rightButtonClicked {
5160 reloading_ = true;
5161 [self reloadURL];
5162 }
5163
5164 - (NSString *) _rightButtonTitle {
5165 return @"Reload";
5166 }
5167
5168 - (NSString *) rightButtonTitle {
5169 return loading_ ? @"" : [self _rightButtonTitle];
5170 }
5171
5172 - (NSString *) title {
5173 return title_ == nil ? @"Loading" : title_;
5174 }
5175
5176 - (NSString *) backButtonTitle {
5177 return @"Browser";
5178 }
5179
5180 - (void) setPageActive:(BOOL)active {
5181 if (!active)
5182 [indicator_ removeFromSuperview];
5183 else
5184 [[book_ navigationBar] addSubview:indicator_];
5185 }
5186
5187 - (void) resetViewAnimated:(BOOL)animated {
5188 }
5189
5190 - (void) setPushed:(bool)pushed {
5191 pushed_ = pushed;
5192 }
5193
5194 @end
5195 /* }}} */
5196
5197 /* Cydia Book {{{ */
5198 @interface CYBook : RVBook <
5199 ProgressDelegate
5200 > {
5201 _transient Database *database_;
5202 UINavigationBar *overlay_;
5203 UINavigationBar *underlay_;
5204 UIProgressIndicator *indicator_;
5205 UITextLabel *prompt_;
5206 UIProgressBar *progress_;
5207 UINavigationButton *cancel_;
5208 bool updating_;
5209 size_t received_;
5210 NSTimeInterval last_;
5211 }
5212
5213 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5214 - (void) update;
5215 - (BOOL) updating;
5216
5217 @end
5218
5219 @implementation CYBook
5220
5221 - (void) dealloc {
5222 [overlay_ release];
5223 [indicator_ release];
5224 [prompt_ release];
5225 [progress_ release];
5226 [cancel_ release];
5227 [super dealloc];
5228 }
5229
5230 - (NSString *) getTitleForPage:(RVPage *)page {
5231 return Simplify([super getTitleForPage:page]);
5232 }
5233
5234 - (BOOL) updating {
5235 return updating_;
5236 }
5237
5238 - (void) update {
5239 [UIView beginAnimations:nil context:NULL];
5240
5241 CGRect ovrframe = [overlay_ frame];
5242 ovrframe.origin.y = 0;
5243 [overlay_ setFrame:ovrframe];
5244
5245 CGRect barframe = [navbar_ frame];
5246 barframe.origin.y += ovrframe.size.height;
5247 [navbar_ setFrame:barframe];
5248
5249 CGRect trnframe = [transition_ frame];
5250 trnframe.origin.y += ovrframe.size.height;
5251 trnframe.size.height -= ovrframe.size.height;
5252 [transition_ setFrame:trnframe];
5253
5254 [UIView endAnimations];
5255
5256 [indicator_ startAnimation];
5257 [prompt_ setText:@"Updating Database"];
5258 [progress_ setProgress:0];
5259
5260 received_ = 0;
5261 last_ = [NSDate timeIntervalSinceReferenceDate];
5262 updating_ = true;
5263 [overlay_ addSubview:cancel_];
5264
5265 [NSThread
5266 detachNewThreadSelector:@selector(_update)
5267 toTarget:self
5268 withObject:nil
5269 ];
5270 }
5271
5272 - (void) _update_ {
5273 updating_ = false;
5274
5275 [indicator_ stopAnimation];
5276
5277 [UIView beginAnimations:nil context:NULL];
5278
5279 CGRect ovrframe = [overlay_ frame];
5280 ovrframe.origin.y = -ovrframe.size.height;
5281 [overlay_ setFrame:ovrframe];
5282
5283 CGRect barframe = [navbar_ frame];
5284 barframe.origin.y -= ovrframe.size.height;
5285 [navbar_ setFrame:barframe];
5286
5287 CGRect trnframe = [transition_ frame];
5288 trnframe.origin.y -= ovrframe.size.height;
5289 trnframe.size.height += ovrframe.size.height;
5290 [transition_ setFrame:trnframe];
5291
5292 [UIView commitAnimations];
5293
5294 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5295 }
5296
5297 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5298 if ((self = [super initWithFrame:frame]) != nil) {
5299 database_ = database;
5300
5301 CGRect ovrrect = [navbar_ bounds];
5302 ovrrect.size.height = [UINavigationBar defaultSize].height;
5303 ovrrect.origin.y = -ovrrect.size.height;
5304
5305 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5306 [self addSubview:overlay_];
5307
5308 ovrrect.origin.y = frame.size.height;
5309 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5310 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5311 [self addSubview:underlay_];
5312
5313 [overlay_ setBarStyle:1];
5314 [underlay_ setBarStyle:1];
5315
5316 int barstyle = [overlay_ _barStyle:NO];
5317 bool ugly = barstyle == 0;
5318
5319 UIProgressIndicatorStyle style = ugly ?
5320 UIProgressIndicatorStyleMediumBrown :
5321 UIProgressIndicatorStyleMediumWhite;
5322
5323 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5324 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5325 CGRect indrect = {{indoffset, indoffset}, indsize};
5326
5327 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5328 [indicator_ setStyle:style];
5329 [overlay_ addSubview:indicator_];
5330
5331 CGSize prmsize = {215, indsize.height + 4};
5332
5333 CGRect prmrect = {{
5334 indoffset * 2 + indsize.width,
5335 #ifdef __OBJC2__
5336 -1 +
5337 #endif
5338 unsigned(ovrrect.size.height - prmsize.height) / 2
5339 }, prmsize};
5340
5341 UIFont *font = [UIFont systemFontOfSize:15];
5342
5343 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5344
5345 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5346 [prompt_ setBackgroundColor:[UIColor clearColor]];
5347 [prompt_ setFont:font];
5348
5349 [overlay_ addSubview:prompt_];
5350
5351 CGSize prgsize = {75, 100};
5352
5353 CGRect prgrect = {{
5354 ovrrect.size.width - prgsize.width - 10,
5355 (ovrrect.size.height - prgsize.height) / 2
5356 } , prgsize};
5357
5358 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5359 [progress_ setStyle:0];
5360 [overlay_ addSubview:progress_];
5361
5362 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5363 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5364
5365 CGRect frame = [cancel_ frame];
5366 frame.size.width = 65;
5367 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5368 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5369 [cancel_ setFrame:frame];
5370
5371 [cancel_ setBarStyle:barstyle];
5372 } return self;
5373 }
5374
5375 - (void) _onCancel {
5376 updating_ = false;
5377 [cancel_ removeFromSuperview];
5378 }
5379
5380 - (void) _update { _pooled
5381 Status status;
5382 status.setDelegate(self);
5383
5384 [database_ updateWithStatus:status];
5385
5386 [self
5387 performSelectorOnMainThread:@selector(_update_)
5388 withObject:nil
5389 waitUntilDone:NO
5390 ];
5391 }
5392
5393 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5394 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5395 }
5396
5397 - (void) setProgressTitle:(NSString *)title {
5398 [self
5399 performSelectorOnMainThread:@selector(_setProgressTitle:)
5400 withObject:title
5401 waitUntilDone:YES
5402 ];
5403 }
5404
5405 - (void) setProgressPercent:(float)percent {
5406 [self
5407 performSelectorOnMainThread:@selector(_setProgressPercent:)
5408 withObject:[NSNumber numberWithFloat:percent]
5409 waitUntilDone:YES
5410 ];
5411 }
5412
5413 - (void) startProgress {
5414 }
5415
5416 - (void) addProgressOutput:(NSString *)output {
5417 [self
5418 performSelectorOnMainThread:@selector(_addProgressOutput:)
5419 withObject:output
5420 waitUntilDone:YES
5421 ];
5422 }
5423
5424 - (bool) isCancelling:(size_t)received {
5425 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5426 if (received_ != received) {
5427 received_ = received;
5428 last_ = now;
5429 } else if (now - last_ > 15)
5430 return true;
5431 return !updating_;
5432 }
5433
5434 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5435 [sheet dismiss];
5436 }
5437
5438 - (void) _setProgressTitle:(NSString *)title {
5439 [prompt_ setText:title];
5440 }
5441
5442 - (void) _setProgressPercent:(NSNumber *)percent {
5443 [progress_ setProgress:[percent floatValue]];
5444 }
5445
5446 - (void) _addProgressOutput:(NSString *)output {
5447 }
5448
5449 @end
5450 /* }}} */
5451 /* Cydia:// Protocol {{{ */
5452 @interface CydiaURLProtocol : NSURLProtocol {
5453 }
5454
5455 @end
5456
5457 @implementation CydiaURLProtocol
5458
5459 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5460 NSURL *url([request URL]);
5461 if (url == nil)
5462 return NO;
5463 NSString *scheme([[url scheme] lowercaseString]);
5464 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5465 return NO;
5466 return YES;
5467 }
5468
5469 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5470 return request;
5471 }
5472
5473 - (void) startLoading {
5474 id<NSURLProtocolClient> client([self client]);
5475 NSURLRequest *request([self request]);
5476
5477 NSURL *url([request URL]);
5478 NSString *href([url absoluteString]);
5479
5480 NSString *path([href substringFromIndex:8]);
5481 NSRange slash([path rangeOfString:@"/"]);
5482
5483 NSString *command;
5484 if (slash.location == NSNotFound) {
5485 command = path;
5486 path = nil;
5487 } else {
5488 command = [path substringToIndex:slash.location];
5489 path = [path substringFromIndex:(slash.location + 1)];
5490 }
5491
5492 Database *database([Database sharedInstance]);
5493
5494 if ([command isEqualToString:@"package-icon"]) {
5495 if (path == nil)
5496 goto fail;
5497 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5498 Package *package([database packageWithName:path]);
5499 if (package == nil)
5500 goto fail;
5501
5502 UIImage *icon([package icon]);
5503
5504 NSData *data(UIImagePNGRepresentation(icon));
5505
5506 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5507 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5508 [client URLProtocol:self didLoadData:data];
5509 [client URLProtocolDidFinishLoading:self];
5510 } else if ([command isEqualToString:@"section-icon"]) {
5511 if (path == nil)
5512 goto fail;
5513 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5514 NSString *section(Simplify(path));
5515
5516 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5517 if (icon == nil)
5518 icon = [UIImage applicationImageNamed:@"unknown.png"];
5519
5520 NSData *data(UIImagePNGRepresentation(icon));
5521
5522 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5523 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5524 [client URLProtocol:self didLoadData:data];
5525 [client URLProtocolDidFinishLoading:self];
5526 } else fail: {
5527 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5528 }
5529 }
5530
5531 - (void) stopLoading {
5532 }
5533
5534 @end
5535 /* }}} */
5536
5537 /* Install View {{{ */
5538 @interface InstallView : RVPage {
5539 _transient Database *database_;
5540 NSMutableArray *sections_;
5541 NSMutableArray *filtered_;
5542 UITransitionView *transition_;
5543 UITable *list_;
5544 UIView *accessory_;
5545 BOOL editing_;
5546 }
5547
5548 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5549 - (void) reloadData;
5550 - (void) resetView;
5551
5552 @end
5553
5554 @implementation InstallView
5555
5556 - (void) dealloc {
5557 [list_ setDataSource:nil];
5558 [list_ setDelegate:nil];
5559
5560 [sections_ release];
5561 [filtered_ release];
5562 [transition_ release];
5563 [list_ release];
5564 [accessory_ release];
5565 [super dealloc];
5566 }
5567
5568 - (int) numberOfRowsInTable:(UITable *)table {
5569 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5570 }
5571
5572 - (float) table:(UITable *)table heightForRow:(int)row {
5573 return 45;
5574 }
5575
5576 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5577 if (reusing == nil)
5578 reusing = [[[SectionCell alloc] init] autorelease];
5579 [(SectionCell *)reusing setSection:(editing_ ?
5580 [sections_ objectAtIndex:row] :
5581 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5582 ) editing:editing_];
5583 return reusing;
5584 }
5585
5586 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5587 return !editing_;
5588 }
5589
5590 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5591 return !editing_;
5592 }
5593
5594 - (void) tableRowSelected:(NSNotification *)notification {
5595 int row = [[notification object] selectedRow];
5596 if (row == INT_MAX)
5597 return;
5598
5599 Section *section;
5600 NSString *name;
5601 NSString *title;
5602
5603 if (row == 0) {
5604 section = nil;
5605 name = nil;
5606 title = @"All Packages";
5607 } else {
5608 section = [filtered_ objectAtIndex:(row - 1)];
5609 name = [section name];
5610
5611 if (name != nil)
5612 title = name;
5613 else {
5614 name = @"";
5615 title = @"(No Section)";
5616 }
5617 }
5618
5619 PackageTable *table = [[[PackageTable alloc]
5620 initWithBook:book_
5621 database:database_
5622 title:title
5623 filter:@selector(isVisiblyUninstalledInSection:)
5624 with:name
5625 ] autorelease];
5626
5627 [table setDelegate:delegate_];
5628
5629 [book_ pushPage:table];
5630 }
5631
5632 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5633 if ((self = [super initWithBook:book]) != nil) {
5634 database_ = database;
5635
5636 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5637 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5638
5639 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5640 [self addSubview:transition_];
5641
5642 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5643 [transition_ transition:0 toView:list_];
5644
5645 UITableColumn *column = [[[UITableColumn alloc]
5646 initWithTitle:@"Name"
5647 identifier:@"name"
5648 width:[self frame].size.width
5649 ] autorelease];
5650
5651 [list_ setDataSource:self];
5652 [list_ setSeparatorStyle:1];
5653 [list_ addTableColumn:column];
5654 [list_ setDelegate:self];
5655 [list_ setReusesTableCells:YES];
5656
5657 [self reloadData];
5658
5659 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5660 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5661 } return self;
5662 }
5663
5664 - (void) reloadData {
5665 NSArray *packages = [database_ packages];
5666
5667 [sections_ removeAllObjects];
5668 [filtered_ removeAllObjects];
5669
5670 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5671 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5672
5673 _trace();
5674 for (size_t i(0); i != [packages count]; ++i) {
5675 Package *package([packages objectAtIndex:i]);
5676 NSString *name([package section]);
5677
5678 if (name != nil) {
5679 Section *section([sections objectForKey:name]);
5680 if (section == nil) {
5681 section = [[[Section alloc] initWithName:name] autorelease];
5682 [sections setObject:section forKey:name];
5683 }
5684 }
5685
5686 if ([package valid] && [package installed] == nil && [package visible])
5687 [filtered addObject:package];
5688 }
5689 _trace();
5690
5691 [sections_ addObjectsFromArray:[sections allValues]];
5692 [sections_ sortUsingSelector:@selector(compareByName:)];
5693
5694 _trace();
5695 [filtered sortUsingSelector:@selector(compareBySection:)];
5696 _trace();
5697
5698 Section *section = nil;
5699 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5700 Package *package = [filtered objectAtIndex:offset];
5701 NSString *name = [package section];
5702
5703 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5704 section = name == nil ?
5705 [[[Section alloc] initWithName:nil] autorelease] :
5706 [sections objectForKey:name];
5707 [filtered_ addObject:section];
5708 }
5709
5710 [section addToCount];
5711 }
5712 _trace();
5713
5714 [list_ reloadData];
5715 _trace();
5716 }
5717
5718 - (void) resetView {
5719 if (editing_)
5720 [self _rightButtonClicked];
5721 }
5722
5723 - (void) resetViewAnimated:(BOOL)animated {
5724 [list_ resetViewAnimated:animated];
5725 }
5726
5727 - (void) _rightButtonClicked {
5728 if ((editing_ = !editing_))
5729 [list_ reloadData];
5730 else
5731 [delegate_ updateData];
5732 [book_ reloadTitleForPage:self];
5733 [book_ reloadButtonsForPage:self];
5734 }
5735
5736 - (NSString *) title {
5737 return editing_ ? @"Section Visibility" : @"Install by Section";
5738 }
5739
5740 - (NSString *) backButtonTitle {
5741 return @"Sections";
5742 }
5743
5744 - (NSString *) rightButtonTitle {
5745 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5746 }
5747
5748 - (UINavigationButtonStyle) rightButtonStyle {
5749 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5750 }
5751
5752 - (UIView *) accessoryView {
5753 return accessory_;
5754 }
5755
5756 @end
5757 /* }}} */
5758 /* Changes View {{{ */
5759 @interface ChangesView : RVPage {
5760 _transient Database *database_;
5761 NSMutableArray *packages_;
5762 NSMutableArray *sections_;
5763 UISectionList *list_;
5764 unsigned upgrades_;
5765 }
5766
5767 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5768 - (void) reloadData;
5769
5770 @end
5771
5772 @implementation ChangesView
5773
5774 - (void) dealloc {
5775 [[list_ table] setDelegate:nil];
5776 [list_ setDataSource:nil];
5777
5778 [packages_ release];
5779 [sections_ release];
5780 [list_ release];
5781 [super dealloc];
5782 }
5783
5784 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5785 return [sections_ count];
5786 }
5787
5788 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5789 return [[sections_ objectAtIndex:section] name];
5790 }
5791
5792 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5793 return [[sections_ objectAtIndex:section] row];
5794 }
5795
5796 - (int) numberOfRowsInTable:(UITable *)table {
5797 return [packages_ count];
5798 }
5799
5800 - (float) table:(UITable *)table heightForRow:(int)row {
5801 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5802 }
5803
5804 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5805 if (reusing == nil)
5806 reusing = [[[PackageCell alloc] init] autorelease];
5807 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5808 return reusing;
5809 }
5810
5811 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5812 return NO;
5813 }
5814
5815 - (void) tableRowSelected:(NSNotification *)notification {
5816 int row = [[notification object] selectedRow];
5817 if (row == INT_MAX)
5818 return;
5819 Package *package = [packages_ objectAtIndex:row];
5820 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5821 [view setDelegate:delegate_];
5822 [view setPackage:package];
5823 [book_ pushPage:view];
5824 }
5825
5826 - (void) _leftButtonClicked {
5827 [(CYBook *)book_ update];
5828 [self reloadButtons];
5829 }
5830
5831 - (void) _rightButtonClicked {
5832 [delegate_ distUpgrade];
5833 }
5834
5835 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5836 if ((self = [super initWithBook:book]) != nil) {
5837 database_ = database;
5838
5839 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5840 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5841
5842 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5843 [self addSubview:list_];
5844
5845 [list_ setShouldHideHeaderInShortLists:NO];
5846 [list_ setDataSource:self];
5847 //[list_ setSectionListStyle:1];
5848
5849 UITableColumn *column = [[[UITableColumn alloc]
5850 initWithTitle:@"Name"
5851 identifier:@"name"
5852 width:[self frame].size.width
5853 ] autorelease];
5854
5855 UITable *table = [list_ table];
5856 [table setSeparatorStyle:1];
5857 [table addTableColumn:column];
5858 [table setDelegate:self];
5859 [table setReusesTableCells:YES];
5860
5861 [self reloadData];
5862
5863 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5864 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5865 } return self;
5866 }
5867
5868 - (void) reloadData {
5869 NSArray *packages = [database_ packages];
5870
5871 [packages_ removeAllObjects];
5872 [sections_ removeAllObjects];
5873
5874 _trace();
5875 for (size_t i(0); i != [packages count]; ++i) {
5876 Package *package([packages objectAtIndex:i]);
5877
5878 if (
5879 [package installed] == nil && [package valid] && [package visible] ||
5880 [package upgradableAndEssential:NO]
5881 )
5882 [packages_ addObject:package];
5883 }
5884
5885 _trace();
5886 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5887 _trace();
5888
5889 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5890 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5891 Section *section = nil;
5892 NSDate *last = nil;
5893
5894 upgrades_ = 0;
5895 bool unseens = false;
5896
5897 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5898
5899 _trace();
5900 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5901 Package *package = [packages_ objectAtIndex:offset];
5902
5903 if (![package upgradableAndEssential:YES]) {
5904 unseens = true;
5905 NSDate *seen = [package seen];
5906
5907 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5908 last = seen;
5909
5910 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5911 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5912 [sections_ addObject:section];
5913 [name release];
5914 }
5915
5916 [section addToCount];
5917 } else if ([package ignored])
5918 [ignored addToCount];
5919 else {
5920 ++upgrades_;
5921 [upgradable addToCount];
5922 }
5923 }
5924 _trace();
5925
5926 CFRelease(formatter);
5927
5928 if (unseens) {
5929 Section *last = [sections_ lastObject];
5930 size_t count = [last count];
5931 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5932 [sections_ removeLastObject];
5933 }
5934
5935 if ([ignored count] != 0)
5936 [sections_ insertObject:ignored atIndex:0];
5937 if (upgrades_ != 0)
5938 [sections_ insertObject:upgradable atIndex:0];
5939
5940 [list_ reloadData];
5941 [self reloadButtons];
5942 }
5943
5944 - (void) resetViewAnimated:(BOOL)animated {
5945 [list_ resetViewAnimated:animated];
5946 }
5947
5948 - (NSString *) leftButtonTitle {
5949 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5950 }
5951
5952 - (NSString *) rightButtonTitle {
5953 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5954 }
5955
5956 - (NSString *) title {
5957 return @"Changes";
5958 }
5959
5960 @end
5961 /* }}} */
5962 /* Search View {{{ */
5963 @protocol SearchViewDelegate
5964 - (void) showKeyboard:(BOOL)show;
5965 @end
5966
5967 @interface SearchView : RVPage {
5968 UIView *accessory_;
5969 UISearchField *field_;
5970 UITransitionView *transition_;
5971 PackageTable *table_;
5972 UIPreferencesTable *advanced_;
5973 UIView *dimmed_;
5974 bool flipped_;
5975 bool reload_;
5976 }
5977
5978 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5979 - (void) reloadData;
5980
5981 @end
5982
5983 @implementation SearchView
5984
5985 - (void) dealloc {
5986 [field_ setDelegate:nil];
5987
5988 [accessory_ release];
5989 [field_ release];
5990 [transition_ release];
5991 [table_ release];
5992 [advanced_ release];
5993 [dimmed_ release];
5994 [super dealloc];
5995 }
5996
5997 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5998 return 1;
5999 }
6000
6001 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6002 switch (group) {
6003 case 0: return @"Advanced Search (Coming Soon!)";
6004
6005 default: _assert(false);
6006 }
6007 }
6008
6009 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6010 switch (group) {
6011 case 0: return 0;
6012
6013 default: _assert(false);
6014 }
6015 }
6016
6017 - (void) _showKeyboard:(BOOL)show {
6018 CGSize keysize = [UIKeyboard defaultSize];
6019 CGRect keydown = [book_ pageBounds];
6020 CGRect keyup = keydown;
6021 keyup.size.height -= keysize.height - ButtonBarHeight_;
6022
6023 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6024
6025 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6026 [animation setSignificantRectFields:8];
6027
6028 if (show) {
6029 [animation setStartFrame:keydown];
6030 [animation setEndFrame:keyup];
6031 } else {
6032 [animation setStartFrame:keyup];
6033 [animation setEndFrame:keydown];
6034 }
6035
6036 UIAnimator *animator = [UIAnimator sharedAnimator];
6037
6038 [animator
6039 addAnimations:[NSArray arrayWithObjects:animation, nil]
6040 withDuration:(KeyboardTime_ - delay)
6041 start:!show
6042 ];
6043
6044 if (show)
6045 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6046
6047 [delegate_ showKeyboard:show];
6048 }
6049
6050 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6051 [self _showKeyboard:YES];
6052 }
6053
6054 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6055 [self _showKeyboard:NO];
6056 }
6057
6058 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6059 if (reload_) {
6060 NSString *text([field_ text]);
6061 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6062 [self reloadData];
6063 reload_ = false;
6064 }
6065 }
6066
6067 - (void) textFieldClearButtonPressed:(UITextField *)field {
6068 reload_ = true;
6069 }
6070
6071 - (void) keyboardInputShouldDelete:(id)input {
6072 reload_ = true;
6073 }
6074
6075 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6076 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6077 reload_ = true;
6078 return YES;
6079 } else {
6080 [field_ resignFirstResponder];
6081 return NO;
6082 }
6083 }
6084
6085 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6086 if ((self = [super initWithBook:book]) != nil) {
6087 CGRect pageBounds = [book_ pageBounds];
6088
6089 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
6090 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
6091 [self addSubview:pinstripe];*/
6092
6093 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6094 [self addSubview:transition_];
6095
6096 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6097
6098 [advanced_ setReusesTableCells:YES];
6099 [advanced_ setDataSource:self];
6100 [advanced_ reloadData];
6101
6102 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6103 CGColor dimmed(space_, 0, 0, 0, 0.5);
6104 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6105
6106 table_ = [[PackageTable alloc]
6107 initWithBook:book
6108 database:database
6109 title:nil
6110 filter:@selector(isUnfilteredAndSearchedForBy:)
6111 with:nil
6112 ];
6113
6114 [table_ setShouldHideHeaderInShortLists:NO];
6115 [transition_ transition:0 toView:table_];
6116
6117 CGRect cnfrect = {{
6118 #ifdef __OBJC2__
6119 6 +
6120 #endif
6121 1, 38}, {17, 18}};
6122
6123 CGRect area;
6124 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6125 area.origin.y = 1;
6126
6127 area.size.width =
6128 #ifdef __OBJC2__
6129 8 +
6130 #endif
6131 [self bounds].size.width - area.origin.x - 18;
6132
6133 area.size.height = [UISearchField defaultHeight];
6134
6135 field_ = [[UISearchField alloc] initWithFrame:area];
6136
6137 UIFont *font = [UIFont systemFontOfSize:16];
6138 [field_ setFont:font];
6139
6140 [field_ setPlaceholder:@"Package Names & Descriptions"];
6141 [field_ setDelegate:self];
6142
6143 [field_ setPaddingTop:5];
6144
6145 UITextInputTraits *traits = [field_ textInputTraits];
6146 [traits setAutocapitalizationType:0];
6147 [traits setAutocorrectionType:1];
6148 [traits setReturnKeyType:6];
6149
6150 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6151
6152 accessory_ = [[UIView alloc] initWithFrame:accrect];
6153 [accessory_ addSubview:field_];
6154
6155 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6156 [configure setShowPressFeedback:YES];
6157 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6158 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6159 [accessory_ addSubview:configure];*/
6160
6161 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6162 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6163 } return self;
6164 }
6165
6166 - (void) flipPage {
6167 #ifndef __OBJC2__
6168 LKAnimation *animation = [LKTransition animation];
6169 [animation setType:@"oglFlip"];
6170 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6171 [animation setFillMode:@"extended"];
6172 [animation setTransitionFlags:3];
6173 [animation setDuration:10];
6174 [animation setSpeed:0.35];
6175 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6176 [[transition_ _layer] addAnimation:animation forKey:0];
6177 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6178 flipped_ = !flipped_;
6179 #endif
6180 }
6181
6182 - (void) configurePushed {
6183 [field_ resignFirstResponder];
6184 [self flipPage];
6185 }
6186
6187 - (void) resetViewAnimated:(BOOL)animated {
6188 if (flipped_)
6189 [self flipPage];
6190 [table_ resetViewAnimated:animated];
6191 }
6192
6193 - (void) _reloadData {
6194 }
6195
6196 - (void) reloadData {
6197 if (flipped_)
6198 [self flipPage];
6199 [table_ setObject:[field_ text]];
6200 [table_ reloadData];
6201 [table_ resetCursor];
6202 }
6203
6204 - (UIView *) accessoryView {
6205 return accessory_;
6206 }
6207
6208 - (NSString *) title {
6209 return nil;
6210 }
6211
6212 - (NSString *) backButtonTitle {
6213 return @"Search";
6214 }
6215
6216 - (void) setDelegate:(id)delegate {
6217 [table_ setDelegate:delegate];
6218 [super setDelegate:delegate];
6219 }
6220
6221 @end
6222 /* }}} */
6223
6224 @interface SettingsView : RVPage {
6225 _transient Database *database_;
6226 NSString *name_;
6227 Package *package_;
6228 UIPreferencesTable *table_;
6229 _UISwitchSlider *subscribedSwitch_;
6230 _UISwitchSlider *ignoredSwitch_;
6231 UIPreferencesControlTableCell *subscribedCell_;
6232 UIPreferencesControlTableCell *ignoredCell_;
6233 }
6234
6235 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6236
6237 @end
6238
6239 @implementation SettingsView
6240
6241 - (void) dealloc {
6242 [table_ setDataSource:nil];
6243
6244 [name_ release];
6245 if (package_ != nil)
6246 [package_ release];
6247 [table_ release];
6248 [subscribedSwitch_ release];
6249 [ignoredSwitch_ release];
6250 [subscribedCell_ release];
6251 [ignoredCell_ release];
6252 [super dealloc];
6253 }
6254
6255 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6256 if (package_ == nil)
6257 return 0;
6258
6259 return 2;
6260 }
6261
6262 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6263 if (package_ == nil)
6264 return nil;
6265
6266 switch (group) {
6267 case 0: return nil;
6268 case 1: return nil;
6269
6270 default: _assert(false);
6271 }
6272
6273 return nil;
6274 }
6275
6276 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6277 if (package_ == nil)
6278 return NO;
6279
6280 switch (group) {
6281 case 0: return NO;
6282 case 1: return YES;
6283
6284 default: _assert(false);
6285 }
6286
6287 return NO;
6288 }
6289
6290 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6291 if (package_ == nil)
6292 return 0;
6293
6294 switch (group) {
6295 case 0: return 1;
6296 case 1: return 1;
6297
6298 default: _assert(false);
6299 }
6300
6301 return 0;
6302 }
6303
6304 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6305 if (package_ == nil)
6306 return;
6307
6308 _UISwitchSlider *slider([cell control]);
6309 BOOL value([slider value] != 0);
6310 NSMutableDictionary *metadata([package_ metadata]);
6311
6312 BOOL before;
6313 if (NSNumber *number = [metadata objectForKey:key])
6314 before = [number boolValue];
6315 else
6316 before = NO;
6317
6318 if (value != before) {
6319 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6320 Changed_ = true;
6321 [delegate_ updateData];
6322 }
6323 }
6324
6325 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6326 [self onSomething:cell withKey:@"IsSubscribed"];
6327 }
6328
6329 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6330 [self onSomething:cell withKey:@"IsIgnored"];
6331 }
6332
6333 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6334 if (package_ == nil)
6335 return nil;
6336
6337 switch (group) {
6338 case 0: switch (row) {
6339 case 0:
6340 return subscribedCell_;
6341 case 1:
6342 return ignoredCell_;
6343 default: _assert(false);
6344 } break;
6345
6346 case 1: switch (row) {
6347 case 0: {
6348 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6349 [cell setShowSelection:NO];
6350 [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."];
6351 return cell;
6352 }
6353
6354 default: _assert(false);
6355 } break;
6356
6357 default: _assert(false);
6358 }
6359
6360 return nil;
6361 }
6362
6363 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6364 if ((self = [super initWithBook:book])) {
6365 database_ = database;
6366 name_ = [package retain];
6367
6368 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6369 [self addSubview:table_];
6370
6371 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6372 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6373
6374 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6375 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6376
6377 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6378 [subscribedCell_ setShowSelection:NO];
6379 [subscribedCell_ setTitle:@"Show All Changes"];
6380 [subscribedCell_ setControl:subscribedSwitch_];
6381
6382 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6383 [ignoredCell_ setShowSelection:NO];
6384 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6385 [ignoredCell_ setControl:ignoredSwitch_];
6386
6387 [table_ setDataSource:self];
6388 [self reloadData];
6389 } return self;
6390 }
6391
6392 - (void) resetViewAnimated:(BOOL)animated {
6393 [table_ resetViewAnimated:animated];
6394 }
6395
6396 - (void) reloadData {
6397 if (package_ != nil)
6398 [package_ autorelease];
6399 package_ = [database_ packageWithName:name_];
6400 if (package_ != nil) {
6401 [package_ retain];
6402 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6403 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6404 }
6405
6406 [table_ reloadData];
6407 }
6408
6409 - (NSString *) title {
6410 return @"Settings";
6411 }
6412
6413 @end
6414
6415 /* Signature View {{{ */
6416 @interface SignatureView : BrowserView {
6417 _transient Database *database_;
6418 NSString *package_;
6419 }
6420
6421 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6422
6423 @end
6424
6425 @implementation SignatureView
6426
6427 - (void) dealloc {
6428 [package_ release];
6429 [super dealloc];
6430 }
6431
6432 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6433 // XXX: dude!
6434 [super webView:sender didClearWindowObject:window forFrame:frame];
6435 }
6436
6437 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6438 if ((self = [super initWithBook:book]) != nil) {
6439 database_ = database;
6440 package_ = [package retain];
6441 [self reloadData];
6442 } return self;
6443 }
6444
6445 - (void) resetViewAnimated:(BOOL)animated {
6446 }
6447
6448 - (void) reloadData {
6449 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6450 }
6451
6452 @end
6453 /* }}} */
6454
6455 @interface Cydia : UIApplication <
6456 ConfirmationViewDelegate,
6457 ProgressViewDelegate,
6458 SearchViewDelegate,
6459 CydiaDelegate
6460 > {
6461 UIWindow *window_;
6462
6463 UIView *underlay_;
6464 UIView *overlay_;
6465 CYBook *book_;
6466 UIToolbar *buttonbar_;
6467
6468 RVBook *confirm_;
6469
6470 NSMutableArray *essential_;
6471 NSMutableArray *broken_;
6472
6473 Database *database_;
6474 ProgressView *progress_;
6475
6476 unsigned tag_;
6477
6478 UIKeyboard *keyboard_;
6479 UIProgressHUD *hud_;
6480
6481 InstallView *install_;
6482 ChangesView *changes_;
6483 ManageView *manage_;
6484 SearchView *search_;
6485 }
6486
6487 @end
6488
6489 @implementation Cydia
6490
6491 - (void) _loaded {
6492 if ([broken_ count] != 0) {
6493 int count = [broken_ count];
6494
6495 UIActionSheet *sheet = [[[UIActionSheet alloc]
6496 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6497 buttons:[NSArray arrayWithObjects:
6498 @"Forcibly Clear",
6499 @"Ignore (Temporary)",
6500 nil]
6501 defaultButtonIndex:0
6502 delegate:self
6503 context:@"fixhalf"
6504 ] autorelease];
6505
6506 [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."];
6507 [sheet popupAlertAnimated:YES];
6508 } else if (!Ignored_ && [essential_ count] != 0) {
6509 int count = [essential_ count];
6510
6511 UIActionSheet *sheet = [[[UIActionSheet alloc]
6512 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6513 buttons:[NSArray arrayWithObjects:
6514 @"Upgrade Essential",
6515 @"Complete Upgrade",
6516 @"Ignore (Temporary)",
6517 nil]
6518 defaultButtonIndex:0
6519 delegate:self
6520 context:@"upgrade"
6521 ] autorelease];
6522
6523 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6524 [sheet popupAlertAnimated:YES];
6525 }
6526 }
6527
6528 - (void) _reloadData {
6529 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6530 [hud setText:@"Reloading Data"];
6531 [overlay_ addSubview:hud];
6532 [hud show:YES];*/
6533
6534 [database_ reloadData];
6535
6536 size_t changes(0);
6537
6538 [essential_ removeAllObjects];
6539 [broken_ removeAllObjects];
6540
6541 NSArray *packages = [database_ packages];
6542 for (Package *package in packages) {
6543 if ([package half])
6544 [broken_ addObject:package];
6545 if ([package upgradableAndEssential:NO]) {
6546 if ([package essential])
6547 [essential_ addObject:package];
6548 ++changes;
6549 }
6550 }
6551
6552 if (changes != 0) {
6553 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6554 [buttonbar_ setBadgeValue:badge forButton:3];
6555 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6556 [buttonbar_ setBadgeAnimated:YES forButton:3];
6557 [self setApplicationBadge:badge];
6558 } else {
6559 [buttonbar_ setBadgeValue:nil forButton:3];
6560 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6561 [buttonbar_ setBadgeAnimated:NO forButton:3];
6562 [self removeApplicationBadge];
6563 }
6564
6565 [self updateData];
6566
6567 #if !ForSaurik
6568 if ([packages count] == 0);
6569 else if (Loaded_)
6570 #endif
6571 [self _loaded];
6572 #if !ForSaurik
6573 else {
6574 Loaded_ = YES;
6575 [book_ update];
6576 }
6577 #endif
6578
6579 /*[hud show:NO];
6580 [hud removeFromSuperview];*/
6581 }
6582
6583 - (void) _saveConfig {
6584 if (Changed_) {
6585 _trace();
6586 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6587 _trace();
6588 Changed_ = false;
6589 }
6590 }
6591
6592 - (void) updateData {
6593 [self _saveConfig];
6594
6595 /* XXX: this is just stupid */
6596 if (tag_ != 2 && install_ != nil)
6597 [install_ reloadData];
6598 if (tag_ != 3 && changes_ != nil)
6599 [changes_ reloadData];
6600 if (tag_ != 5 && search_ != nil)
6601 [search_ reloadData];
6602
6603 [book_ reloadData];
6604 }
6605
6606 - (void) update_ {
6607 [database_ update];
6608 }
6609
6610 - (void) syncData {
6611 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6612 _assert(file != NULL);
6613
6614 NSArray *keys = [Sources_ allKeys];
6615
6616 for (int i(0), e([keys count]); i != e; ++i) {
6617 NSString *key = [keys objectAtIndex:i];
6618 NSDictionary *source = [Sources_ objectForKey:key];
6619
6620 fprintf(file, "%s %s %s\n",
6621 [[source objectForKey:@"Type"] UTF8String],
6622 [[source objectForKey:@"URI"] UTF8String],
6623 [[source objectForKey:@"Distribution"] UTF8String]
6624 );
6625 }
6626
6627 fclose(file);
6628
6629 [self _saveConfig];
6630
6631 [progress_
6632 detachNewThreadSelector:@selector(update_)
6633 toTarget:self
6634 withObject:nil
6635 title:@"Updating Sources"
6636 ];
6637 }
6638
6639 - (void) reloadData {
6640 @synchronized (self) {
6641 if (confirm_ == nil)
6642 [self _reloadData];
6643 }
6644 }
6645
6646 - (void) resolve {
6647 pkgProblemResolver *resolver = [database_ resolver];
6648
6649 resolver->InstallProtect();
6650 if (!resolver->Resolve(true))
6651 _error->Discard();
6652 }
6653
6654 - (void) perform {
6655 [database_ prepare];
6656
6657 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6658 [confirm_ setDelegate:self];
6659
6660 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6661 [page setDelegate:self];
6662
6663 [confirm_ setPage:page];
6664 [underlay_ popSubview:confirm_];
6665 }
6666
6667 - (void) installPackage:(Package *)package {
6668 @synchronized (self) {
6669 [package install];
6670 [self resolve];
6671 [self perform];
6672 }
6673 }
6674
6675 - (void) removePackage:(Package *)package {
6676 @synchronized (self) {
6677 [package remove];
6678 [self resolve];
6679 [self perform];
6680 }
6681 }
6682
6683 - (void) distUpgrade {
6684 @synchronized (self) {
6685 [database_ upgrade];
6686 [self perform];
6687 }
6688 }
6689
6690 - (void) cancel {
6691 @synchronized (self) {
6692 [self _reloadData];
6693 if (confirm_ != nil) {
6694 [confirm_ release];
6695 confirm_ = nil;
6696 }
6697 }
6698 }
6699
6700 - (void) confirm {
6701 [overlay_ removeFromSuperview];
6702 reload_ = true;
6703
6704 [progress_
6705 detachNewThreadSelector:@selector(perform)
6706 toTarget:database_
6707 withObject:nil
6708 title:@"Running"
6709 ];
6710 }
6711
6712 - (void) bootstrap_ {
6713 [database_ update];
6714 [database_ upgrade];
6715 [database_ prepare];
6716 [database_ perform];
6717 }
6718
6719 - (void) bootstrap {
6720 [progress_
6721 detachNewThreadSelector:@selector(bootstrap_)
6722 toTarget:self
6723 withObject:nil
6724 title:@"Bootstrap Install"
6725 ];
6726 }
6727
6728 - (void) progressViewIsComplete:(ProgressView *)progress {
6729 if (confirm_ != nil) {
6730 [underlay_ addSubview:overlay_];
6731 [confirm_ popFromSuperviewAnimated:NO];
6732 }
6733
6734 [self cancel];
6735 }
6736
6737 - (void) setPage:(RVPage *)page {
6738 [page resetViewAnimated:NO];
6739 [page setDelegate:self];
6740 [book_ setPage:page];
6741 }
6742
6743 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6744 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6745 [browser loadURL:url];
6746 return browser;
6747 }
6748
6749 - (void) _setHomePage {
6750 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6751 }
6752
6753 - (void) buttonBarItemTapped:(id)sender {
6754 unsigned tag = [sender tag];
6755 if (tag == tag_) {
6756 [book_ resetViewAnimated:YES];
6757 return;
6758 } else if (tag_ == 2 && tag != 2)
6759 [install_ resetView];
6760
6761 switch (tag) {
6762 case 1: [self _setHomePage]; break;
6763
6764 case 2: [self setPage:install_]; break;
6765 case 3: [self setPage:changes_]; break;
6766 case 4: [self setPage:manage_]; break;
6767 case 5: [self setPage:search_]; break;
6768
6769 default: _assert(false);
6770 }
6771
6772 tag_ = tag;
6773 }
6774
6775 - (void) applicationWillSuspend {
6776 [database_ clean];
6777 [super applicationWillSuspend];
6778 }
6779
6780 - (void) askForSettings {
6781 UIActionSheet *role = [[[UIActionSheet alloc]
6782 initWithTitle:@"Who Are You?"
6783 buttons:[NSArray arrayWithObjects:
6784 @"User (Graphical Only)",
6785 @"Hacker (+ Command Line)",
6786 @"Developer (No Filters)",
6787 nil]
6788 defaultButtonIndex:-1
6789 delegate:self
6790 context:@"role"
6791 ] autorelease];
6792
6793 [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."];
6794 [role popupAlertAnimated:YES];
6795 }
6796
6797 - (void) finish {
6798 if (hud_ != nil) {
6799 [self setStatusBarShowsProgress:NO];
6800
6801 [hud_ show:NO];
6802 [hud_ removeFromSuperview];
6803 [hud_ autorelease];
6804 hud_ = nil;
6805
6806 pid_t pid = ExecFork();
6807 if (pid == 0) {
6808 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6809 perror("launchctl stop");
6810 }
6811
6812 return;
6813 }
6814
6815 if (Role_ == nil) {
6816 [self askForSettings];
6817 return;
6818 }
6819
6820 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6821
6822 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6823 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6824 0, 0, screenrect.size.width, screenrect.size.height - 48
6825 ) database:database_];
6826
6827 [book_ setDelegate:self];
6828
6829 [overlay_ addSubview:book_];
6830
6831 NSArray *buttonitems = [NSArray arrayWithObjects:
6832 [NSDictionary dictionaryWithObjectsAndKeys:
6833 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6834 @"home-up.png", kUIButtonBarButtonInfo,
6835 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6836 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6837 self, kUIButtonBarButtonTarget,
6838 @"Home", kUIButtonBarButtonTitle,
6839 @"0", kUIButtonBarButtonType,
6840 nil],
6841
6842 [NSDictionary dictionaryWithObjectsAndKeys:
6843 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6844 @"install-up.png", kUIButtonBarButtonInfo,
6845 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6846 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6847 self, kUIButtonBarButtonTarget,
6848 @"Sections", kUIButtonBarButtonTitle,
6849 @"0", kUIButtonBarButtonType,
6850 nil],
6851
6852 [NSDictionary dictionaryWithObjectsAndKeys:
6853 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6854 @"changes-up.png", kUIButtonBarButtonInfo,
6855 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6856 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6857 self, kUIButtonBarButtonTarget,
6858 @"Changes", kUIButtonBarButtonTitle,
6859 @"0", kUIButtonBarButtonType,
6860 nil],
6861
6862 [NSDictionary dictionaryWithObjectsAndKeys:
6863 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6864 @"manage-up.png", kUIButtonBarButtonInfo,
6865 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6866 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6867 self, kUIButtonBarButtonTarget,
6868 @"Manage", kUIButtonBarButtonTitle,
6869 @"0", kUIButtonBarButtonType,
6870 nil],
6871
6872 [NSDictionary dictionaryWithObjectsAndKeys:
6873 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6874 @"search-up.png", kUIButtonBarButtonInfo,
6875 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6876 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6877 self, kUIButtonBarButtonTarget,
6878 @"Search", kUIButtonBarButtonTitle,
6879 @"0", kUIButtonBarButtonType,
6880 nil],
6881 nil];
6882
6883 buttonbar_ = [[UIToolbar alloc]
6884 initInView:overlay_
6885 withFrame:CGRectMake(
6886 0, screenrect.size.height - ButtonBarHeight_,
6887 screenrect.size.width, ButtonBarHeight_
6888 )
6889 withItemList:buttonitems
6890 ];
6891
6892 [buttonbar_ setDelegate:self];
6893 [buttonbar_ setBarStyle:1];
6894 [buttonbar_ setButtonBarTrackingMode:2];
6895
6896 int buttons[5] = {1, 2, 3, 4, 5};
6897 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6898 [buttonbar_ showButtonGroup:0 withDuration:0];
6899
6900 for (int i = 0; i != 5; ++i)
6901 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6902 i * 64 + 2, 1, 60, ButtonBarHeight_
6903 )];
6904
6905 [buttonbar_ showSelectionForButton:1];
6906 [overlay_ addSubview:buttonbar_];
6907
6908 [UIKeyboard initImplementationNow];
6909 CGSize keysize = [UIKeyboard defaultSize];
6910 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6911 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6912 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6913 [overlay_ addSubview:keyboard_];
6914
6915 if (!bootstrap_)
6916 [underlay_ addSubview:overlay_];
6917
6918 [self reloadData];
6919
6920 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6921 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6922 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6923
6924 manage_ = (ManageView *) [[self
6925 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6926 withClass:[ManageView class]
6927 ] retain];
6928
6929 if (bootstrap_)
6930 [self bootstrap];
6931 else
6932 [self _setHomePage];
6933 }
6934
6935 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6936 NSString *context = [sheet context];
6937 if ([context isEqualToString:@"fixhalf"])
6938 switch (button) {
6939 case 1:
6940 @synchronized (self) {
6941 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6942 Package *broken = [broken_ objectAtIndex:i];
6943 [broken remove];
6944
6945 NSString *id = [broken id];
6946 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6947 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6948 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6949 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6950 }
6951
6952 [self resolve];
6953 [self perform];
6954 }
6955 break;
6956
6957 case 2:
6958 [broken_ removeAllObjects];
6959 [self _loaded];
6960 break;
6961
6962 default:
6963 _assert(false);
6964 }
6965 else if ([context isEqualToString:@"role"]) {
6966 switch (button) {
6967 case 1: Role_ = @"User"; break;
6968 case 2: Role_ = @"Hacker"; break;
6969 case 3: Role_ = @"Developer"; break;
6970
6971 default:
6972 Role_ = nil;
6973 _assert(false);
6974 }
6975
6976 bool reset = Settings_ != nil;
6977
6978 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6979 Role_, @"Role",
6980 nil];
6981
6982 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6983
6984 Changed_ = true;
6985
6986 if (reset)
6987 [self updateData];
6988 else
6989 [self finish];
6990 } else if ([context isEqualToString:@"upgrade"])
6991 switch (button) {
6992 case 1:
6993 @synchronized (self) {
6994 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6995 Package *essential = [essential_ objectAtIndex:i];
6996 [essential install];
6997 }
6998
6999 [self resolve];
7000 [self perform];
7001 }
7002 break;
7003
7004 case 2:
7005 [self distUpgrade];
7006 break;
7007
7008 case 3:
7009 Ignored_ = YES;
7010 break;
7011
7012 default:
7013 _assert(false);
7014 }
7015
7016 [sheet dismiss];
7017 }
7018
7019 - (void) reorganize { _pooled
7020 system("/usr/libexec/cydia/free.sh");
7021 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7022 }
7023
7024 - (void) applicationSuspend:(__GSEvent *)event {
7025 if (hud_ == nil && ![progress_ isRunning])
7026 [super applicationSuspend:event];
7027 }
7028
7029 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7030 if (hud_ == nil)
7031 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7032 }
7033
7034 - (void) _setSuspended:(BOOL)value {
7035 if (hud_ == nil)
7036 [super _setSuspended:value];
7037 }
7038
7039 - (UIProgressHUD *) addProgressHUD {
7040 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7041 [hud show:YES];
7042 [underlay_ addSubview:hud];
7043 return hud;
7044 }
7045
7046 - (void) openMailToURL:(NSURL *)url {
7047 // XXX: this makes me sad
7048 #if 0
7049 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7050 #else
7051 [UIApp openURL:url];
7052 #endif
7053 }
7054
7055 - (RVPage *) pageForPackage:(NSString *)name {
7056 if (Package *package = [database_ packageWithName:name]) {
7057 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7058 [view setPackage:package];
7059 return view;
7060 } else {
7061 UIActionSheet *sheet = [[[UIActionSheet alloc]
7062 initWithTitle:@"Cannot Locate Package"
7063 buttons:[NSArray arrayWithObjects:@"Close", nil]
7064 defaultButtonIndex:0
7065 delegate:self
7066 context:@"missing"
7067 ] autorelease];
7068
7069 [sheet setBodyText:[NSString stringWithFormat:
7070 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7071 , name]];
7072
7073 [sheet popupAlertAnimated:YES];
7074 return nil;
7075 }
7076 }
7077
7078 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7079 NSString *href = [url absoluteString];
7080
7081 if (tag != NULL)
7082 tag = 0;
7083
7084 if ([href isEqualToString:@"cydia://add-source"])
7085 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7086 else if ([href isEqualToString:@"cydia://sources"])
7087 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7088 else if ([href isEqualToString:@"cydia://packages"])
7089 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7090 else if ([href hasPrefix:@"cydia://url/"])
7091 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
7092 else if ([href hasPrefix:@"cydia://launch/"])
7093 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
7094 else if ([href hasPrefix:@"cydia://package-settings/"])
7095 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
7096 else if ([href hasPrefix:@"cydia://package-signature/"])
7097 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
7098 else if ([href hasPrefix:@"cydia://package/"])
7099 return [self pageForPackage:[href substringFromIndex:16]];
7100 else if ([href hasPrefix:@"cydia://files/"]) {
7101 NSString *name = [href substringFromIndex:14];
7102
7103 if (Package *package = [database_ packageWithName:name]) {
7104 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7105 [files setPackage:package];
7106 return files;
7107 }
7108 }
7109
7110 return nil;
7111 }
7112
7113 - (void) applicationOpenURL:(NSURL *)url {
7114 [super applicationOpenURL:url];
7115 int tag;
7116 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7117 [self setPage:page];
7118 [buttonbar_ showSelectionForButton:tag];
7119 tag_ = tag;
7120 }
7121 }
7122
7123 - (void) applicationDidFinishLaunching:(id)unused {
7124 Font12_ = [[UIFont systemFontOfSize:12] retain];
7125 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7126 Font14_ = [[UIFont systemFontOfSize:14] retain];
7127 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7128 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7129
7130 _assert(pkgInitConfig(*_config));
7131 _assert(pkgInitSystem(*_config, _system));
7132
7133 tag_ = 1;
7134
7135 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7136 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7137
7138 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7139
7140 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7141 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7142
7143 [window_ orderFront:self];
7144 [window_ makeKey:self];
7145 [window_ setHidden:NO];
7146
7147 database_ = [Database sharedInstance];
7148 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7149 [database_ setDelegate:progress_];
7150 [window_ setContentView:progress_];
7151
7152 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7153 [progress_ setContentView:underlay_];
7154
7155 [progress_ resetView];
7156
7157 if (
7158 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7159 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7160 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7161 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7162 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7163 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7164 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7165 ) {
7166 [self setIdleTimerDisabled:YES];
7167
7168 hud_ = [self addProgressHUD];
7169 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
7170
7171 [self setStatusBarShowsProgress:YES];
7172
7173 [NSThread
7174 detachNewThreadSelector:@selector(reorganize)
7175 toTarget:self
7176 withObject:nil
7177 ];
7178 } else
7179 [self finish];
7180 }
7181
7182 /* Web Scripting {{{ */
7183 + (NSString *) webScriptNameForSelector:(SEL)selector {
7184 if (selector == @selector(supports:))
7185 return @"supports";
7186 return nil;
7187 }
7188
7189 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
7190 return selector != @selector(supports:);
7191 }
7192
7193 - (BOOL) supports:(NSString *)feature {
7194 return [feature isEqualToString:@"window.open"];
7195 }
7196 /* }}} */
7197
7198 - (void) showKeyboard:(BOOL)show {
7199 CGSize keysize = [UIKeyboard defaultSize];
7200 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7201 CGRect keyup = keydown;
7202 keyup.origin.y -= keysize.height;
7203
7204 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7205 [animation setSignificantRectFields:2];
7206
7207 if (show) {
7208 [animation setStartFrame:keydown];
7209 [animation setEndFrame:keyup];
7210 [keyboard_ activate];
7211 } else {
7212 [animation setStartFrame:keyup];
7213 [animation setEndFrame:keydown];
7214 [keyboard_ deactivate];
7215 }
7216
7217 [[UIAnimator sharedAnimator]
7218 addAnimations:[NSArray arrayWithObjects:animation, nil]
7219 withDuration:KeyboardTime_
7220 start:YES
7221 ];
7222 }
7223
7224 - (void) slideUp:(UIActionSheet *)alert {
7225 if (Advanced_)
7226 [alert presentSheetFromButtonBar:buttonbar_];
7227 else
7228 [alert presentSheetInView:overlay_];
7229 }
7230
7231 @end
7232
7233 void AddPreferences(NSString *plist) { _pooled
7234 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7235 _assert(settings != NULL);
7236 NSMutableArray *items = [settings objectForKey:@"items"];
7237
7238 bool cydia(false);
7239
7240 for (size_t i(0); i != [items count]; ++i) {
7241 NSMutableDictionary *item([items objectAtIndex:i]);
7242 NSString *label = [item objectForKey:@"label"];
7243 if (label != nil && [label isEqualToString:@"Cydia"]) {
7244 cydia = true;
7245 break;
7246 }
7247 }
7248
7249 if (!cydia) {
7250 for (size_t i(0); i != [items count]; ++i) {
7251 NSDictionary *item([items objectAtIndex:i]);
7252 NSString *label = [item objectForKey:@"label"];
7253 if (label != nil && [label isEqualToString:@"General"]) {
7254 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7255 @"CydiaSettings", @"bundle",
7256 @"PSLinkCell", @"cell",
7257 [NSNumber numberWithBool:YES], @"hasIcon",
7258 [NSNumber numberWithBool:YES], @"isController",
7259 @"Cydia", @"label",
7260 nil] atIndex:(i + 1)];
7261
7262 break;
7263 }
7264 }
7265
7266 _assert([settings writeToFile:plist atomically:YES] == YES);
7267 }
7268 }
7269
7270 /*IMP alloc_;
7271 id Alloc_(id self, SEL selector) {
7272 id object = alloc_(self, selector);
7273 lprintf("[%s]A-%p\n", self->isa->name, object);
7274 return object;
7275 }*/
7276
7277 /*IMP dealloc_;
7278 id Dealloc_(id self, SEL selector) {
7279 id object = dealloc_(self, selector);
7280 lprintf("[%s]D-%p\n", self->isa->name, object);
7281 return object;
7282 }*/
7283
7284 int main(int argc, char *argv[]) { _pooled
7285 bool substrate(false);
7286
7287 if (argc != 0) {
7288 char **args(argv);
7289 int arge(1);
7290
7291 for (int argi(1); argi != argc; ++argi)
7292 if (strcmp(argv[argi], "--") == 0) {
7293 arge = argi;
7294 argv[argi] = argv[0];
7295 argv += argi;
7296 argc -= argi;
7297 break;
7298 }
7299
7300 for (int argi(1); argi != arge; ++argi)
7301 if (strcmp(args[argi], "--bootstrap") == 0)
7302 bootstrap_ = true;
7303 else if (strcmp(args[argi], "--substrate") == 0)
7304 substrate = true;
7305 else
7306 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7307 }
7308
7309 App_ = [[NSBundle mainBundle] bundlePath];
7310 Home_ = NSHomeDirectory();
7311 Locale_ = CFLocaleCopyCurrent();
7312
7313 {
7314 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7315 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7316 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7317 Sounds_Keyboard_ = [keyboard boolValue];
7318 }
7319
7320 setuid(0);
7321 setgid(0);
7322
7323 #if 0 /* XXX: this costs 1.4s of startup performance */
7324 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7325 _assert(errno == ENOENT);
7326 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7327 _assert(errno == ENOENT);
7328 #endif
7329
7330 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7331 alloc_ = alloc->method_imp;
7332 alloc->method_imp = (IMP) &Alloc_;*/
7333
7334 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7335 dealloc_ = dealloc->method_imp;
7336 dealloc->method_imp = (IMP) &Dealloc_;*/
7337
7338 size_t size;
7339
7340 int maxproc;
7341 size = sizeof(maxproc);
7342 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7343 perror("sysctlbyname(\"kern.maxproc\", ?)");
7344 else if (maxproc < 64) {
7345 maxproc = 64;
7346 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7347 perror("sysctlbyname(\"kern.maxproc\", #)");
7348 }
7349
7350 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7351 char *machine = new char[size];
7352 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7353 perror("sysctlbyname(\"hw.machine\", ?)");
7354 else
7355 Machine_ = machine;
7356
7357 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7358
7359 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7360 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7361
7362 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7363 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7364 else {
7365 Settings_ = [Metadata_ objectForKey:@"Settings"];
7366
7367 Packages_ = [Metadata_ objectForKey:@"Packages"];
7368 Sections_ = [Metadata_ objectForKey:@"Sections"];
7369 Sources_ = [Metadata_ objectForKey:@"Sources"];
7370 }
7371
7372 if (Settings_ != nil)
7373 Role_ = [Settings_ objectForKey:@"Role"];
7374
7375 if (Packages_ == nil) {
7376 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7377 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7378 }
7379
7380 if (Sections_ == nil) {
7381 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7382 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7383 }
7384
7385 if (Sources_ == nil) {
7386 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7387 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7388 }
7389
7390 #if RecycleWebViews
7391 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7392 #endif
7393
7394 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7395 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7396
7397 if (access("/User", F_OK) != 0)
7398 system("/usr/libexec/cydia/firmware.sh");
7399
7400 _assert([[NSFileManager defaultManager]
7401 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7402 withIntermediateDirectories:YES
7403 attributes:nil
7404 error:NULL
7405 ]);
7406
7407 space_ = CGColorSpaceCreateDeviceRGB();
7408
7409 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7410 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7411 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7412 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7413 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7414 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7415
7416 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7417
7418 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7419
7420 UIApplicationUseLegacyEvents(YES);
7421 UIKeyboardDisableAutomaticAppearance();
7422
7423 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7424
7425 CGColorSpaceRelease(space_);
7426 CFRelease(Locale_);
7427
7428 return value;
7429 }