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