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