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