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