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