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