]> git.saurik.com Git - cydia.git/blob - Cydia.mm
550482435b46bdcca2f0cafb8ec3d8aa7abb20cb
[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()] lowercaseString] 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 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1260 if (metadata == nil) {
1261 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1262 now_, @"FirstSeen",
1263 nil];
1264
1265 if (solid != nil)
1266 [metadata setObject:solid forKey:@"LastVersion"];
1267 changed = true;
1268 } else {
1269 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1270 NSDate *last([metadata objectForKey:@"LastSeen"]);
1271 NSString *version([metadata objectForKey:@"LastVersion"]);
1272
1273 if (first == nil) {
1274 first = last == nil ? now_ : last;
1275 [metadata setObject:first forKey:@"FirstSeen"];
1276 changed = true;
1277 }
1278
1279 if (solid != nil)
1280 if (version == nil) {
1281 [metadata setObject:solid forKey:@"LastVersion"];
1282 changed = true;
1283 } else if (![version isEqualToString:solid]) {
1284 [metadata setObject:solid forKey:@"LastVersion"];
1285 last = now_;
1286 [metadata setObject:last forKey:@"LastSeen"];
1287 changed = true;
1288 }
1289 }
1290
1291 if (changed) {
1292 [Packages_ setObject:metadata forKey:id_];
1293 Changed_ = true;
1294 }
1295 } return self;
1296 }
1297
1298 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1299 return [[[Package alloc]
1300 initWithIterator:iterator
1301 database:database
1302 ] autorelease];
1303 }
1304
1305 - (pkgCache::PkgIterator) iterator {
1306 return iterator_;
1307 }
1308
1309 - (NSString *) section {
1310 const char *section = iterator_.Section();
1311 if (section == NULL)
1312 return nil;
1313
1314 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1315
1316 lookup:
1317 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1318 if (NSString *rename = [value objectForKey:@"Rename"]) {
1319 name = rename;
1320 goto lookup;
1321 }
1322
1323 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1324 }
1325
1326 - (Address *) maintainer {
1327 if (file_.end())
1328 return nil;
1329 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1330 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1331 }
1332
1333 - (size_t) size {
1334 return version_.end() ? 0 : version_->InstalledSize;
1335 }
1336
1337 - (NSString *) description {
1338 if (file_.end())
1339 return nil;
1340 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1341 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1342
1343 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1344 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1345 if ([lines count] < 2)
1346 return nil;
1347
1348 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1349 for (size_t i(1); i != [lines count]; ++i) {
1350 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1351 [trimmed addObject:trim];
1352 }
1353
1354 return [trimmed componentsJoinedByString:@"\n"];
1355 }
1356
1357 - (NSString *) index {
1358 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1359 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1360 }
1361
1362 - (NSDate *) seen {
1363 NSDictionary *metadata([Packages_ objectForKey:id_]);
1364 bool subscribed;
1365 if (NSNumber *isSubscribed = [metadata objectForKey:@"IsSubscribed"])
1366 subscribed = [isSubscribed boolValue];
1367 else
1368 subscribed = false;
1369 if (subscribed)
1370 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1371 return last;
1372 return [metadata objectForKey:@"FirstSeen"];
1373 }
1374
1375 - (NSString *) latest {
1376 return latest_;
1377 }
1378
1379 - (NSString *) installed {
1380 return installed_;
1381 }
1382
1383 - (BOOL) valid {
1384 return !version_.end();
1385 }
1386
1387 - (BOOL) upgradableAndEssential:(BOOL)essential {
1388 pkgCache::VerIterator current = iterator_.CurrentVer();
1389
1390 if (current.end())
1391 return essential && [self essential];
1392 else {
1393 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1394 return !candidate.end() && candidate != current;
1395 }
1396 }
1397
1398 - (BOOL) essential {
1399 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1400 }
1401
1402 - (BOOL) broken {
1403 return [database_ cache][iterator_].InstBroken();
1404 }
1405
1406 - (BOOL) unfiltered {
1407 NSString *section = [self section];
1408 return section == nil || isSectionVisible(section);
1409 }
1410
1411 - (BOOL) visible {
1412 return [self hasSupportingRole] && [self unfiltered];
1413 }
1414
1415 - (BOOL) half {
1416 unsigned char current = iterator_->CurrentState;
1417 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1418 }
1419
1420 - (BOOL) halfConfigured {
1421 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1422 }
1423
1424 - (BOOL) halfInstalled {
1425 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1426 }
1427
1428 - (BOOL) hasMode {
1429 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1430 return state.Mode != pkgDepCache::ModeKeep;
1431 }
1432
1433 - (NSString *) mode {
1434 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1435
1436 switch (state.Mode) {
1437 case pkgDepCache::ModeDelete:
1438 if ((state.iFlags & pkgDepCache::Purge) != 0)
1439 return @"Purge";
1440 else
1441 return @"Remove";
1442 case pkgDepCache::ModeKeep:
1443 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1444 return nil;
1445 else
1446 return nil;
1447 case pkgDepCache::ModeInstall:
1448 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1449 return @"Reinstall";
1450 else switch (state.Status) {
1451 case -1:
1452 return @"Downgrade";
1453 case 0:
1454 return @"Install";
1455 case 1:
1456 return @"Upgrade";
1457 case 2:
1458 return @"New Install";
1459 default:
1460 _assert(false);
1461 }
1462 default:
1463 _assert(false);
1464 }
1465 }
1466
1467 - (NSString *) id {
1468 return id_;
1469 }
1470
1471 - (NSString *) name {
1472 return name_ == nil ? id_ : name_;
1473 }
1474
1475 - (NSString *) tagline {
1476 return tagline_;
1477 }
1478
1479 - (UIImage *) icon {
1480 NSString *section = [self section];
1481 if (section != nil)
1482 section = Simplify(section);
1483
1484 UIImage *icon(nil);
1485 if (NSString *icon = icon_)
1486 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1487 if (icon == nil) if (section != nil)
1488 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1489 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1490 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1491 if (icon == nil)
1492 icon = [UIImage applicationImageNamed:@"unknown.png"];
1493 return icon;
1494 }
1495
1496 - (NSString *) homepage {
1497 return homepage_;
1498 }
1499
1500 - (NSString *) depiction {
1501 return depiction_;
1502 }
1503
1504 - (Address *) sponsor {
1505 return sponsor_;
1506 }
1507
1508 - (Address *) author {
1509 return author_;
1510 }
1511
1512 - (NSArray *) files {
1513 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1514 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1515
1516 std::ifstream fin;
1517 fin.open([path UTF8String]);
1518 if (!fin.is_open())
1519 return nil;
1520
1521 std::string line;
1522 while (std::getline(fin, line))
1523 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1524
1525 return files;
1526 }
1527
1528 - (NSArray *) relationships {
1529 return relationships_;
1530 }
1531
1532 - (NSArray *) warnings {
1533 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1534 const char *name(iterator_.Name());
1535
1536 size_t length(strlen(name));
1537 if (length < 2) invalid:
1538 [warnings addObject:@"illegal package identifier"];
1539 else for (size_t i(0); i != length; ++i)
1540 if (
1541 (name[i] < 'a' || name[i] > 'z') &&
1542 (name[i] < '0' || name[i] > '9') &&
1543 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1544 ) goto invalid;
1545
1546 if (strcmp(name, "cydia") != 0) {
1547 bool cydia = false;
1548 bool stash = false;
1549
1550 if (NSArray *files = [self files])
1551 for (NSString *file in files)
1552 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1553 cydia = true;
1554 else if (!stash && [file isEqualToString:@"/var/stash"])
1555 stash = true;
1556
1557 if (cydia)
1558 [warnings addObject:@"files installed into Cydia.app"];
1559 if (stash)
1560 [warnings addObject:@"files installed to /var/stash"];
1561 }
1562
1563 return [warnings count] == 0 ? nil : warnings;
1564 }
1565
1566 - (NSArray *) applications {
1567 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1568
1569 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1570
1571 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1572 if (NSArray *files = [self files])
1573 for (NSString *file in files)
1574 if (application_r(file)) {
1575 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1576 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1577 if ([id isEqualToString:me])
1578 continue;
1579
1580 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1581 if (display == nil)
1582 display = application_r[1];
1583
1584 NSString *bundle([file stringByDeletingLastPathComponent]);
1585 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1586 if (icon == nil || [icon length] == 0)
1587 icon = @"icon.png";
1588 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1589
1590 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1591 [applications addObject:application];
1592
1593 [application addObject:id];
1594 [application addObject:display];
1595 [application addObject:url];
1596 }
1597
1598 return [applications count] == 0 ? nil : applications;
1599 }
1600
1601 - (Source *) source {
1602 if (!cached_) {
1603 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1604 cached_ = true;
1605 }
1606
1607 return source_;
1608 }
1609
1610 - (NSString *) role {
1611 return role_;
1612 }
1613
1614 - (BOOL) matches:(NSString *)text {
1615 if (text == nil)
1616 return NO;
1617
1618 NSRange range;
1619
1620 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1621 if (range.location != NSNotFound)
1622 return YES;
1623
1624 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1625 if (range.location != NSNotFound)
1626 return YES;
1627
1628 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1629 if (range.location != NSNotFound)
1630 return YES;
1631
1632 return NO;
1633 }
1634
1635 - (bool) hasSupportingRole {
1636 if (role_ == nil)
1637 return true;
1638 if ([role_ isEqualToString:@"enduser"])
1639 return true;
1640 if ([Role_ isEqualToString:@"User"])
1641 return false;
1642 if ([role_ isEqualToString:@"hacker"])
1643 return true;
1644 if ([Role_ isEqualToString:@"Hacker"])
1645 return false;
1646 if ([role_ isEqualToString:@"developer"])
1647 return true;
1648 if ([Role_ isEqualToString:@"Developer"])
1649 return false;
1650 _assert(false);
1651 }
1652
1653 - (BOOL) hasTag:(NSString *)tag {
1654 return tags_ == nil ? NO : [tags_ containsObject:tag];
1655 }
1656
1657 - (NSString *) primaryPurpose {
1658 for (NSString *tag in tags_)
1659 if ([tag hasPrefix:@"purpose::"])
1660 return [tag substringFromIndex:9];
1661 return nil;
1662 }
1663
1664 - (NSArray *) purposes {
1665 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1666 for (NSString *tag in tags_)
1667 if ([tag hasPrefix:@"purpose::"])
1668 [purposes addObject:[tag substringFromIndex:9]];
1669 return [purposes count] == 0 ? nil : purposes;
1670 }
1671
1672 - (NSComparisonResult) compareByName:(Package *)package {
1673 NSString *lhs = [self name];
1674 NSString *rhs = [package name];
1675
1676 if ([lhs length] != 0 && [rhs length] != 0) {
1677 unichar lhc = [lhs characterAtIndex:0];
1678 unichar rhc = [rhs characterAtIndex:0];
1679
1680 if (isalpha(lhc) && !isalpha(rhc))
1681 return NSOrderedAscending;
1682 else if (!isalpha(lhc) && isalpha(rhc))
1683 return NSOrderedDescending;
1684 }
1685
1686 return [lhs compare:rhs options:CompareOptions_];
1687 }
1688
1689 - (NSComparisonResult) compareBySection:(Package *)package {
1690 NSString *lhs = [self section];
1691 NSString *rhs = [package section];
1692
1693 if (lhs == NULL && rhs != NULL)
1694 return NSOrderedAscending;
1695 else if (lhs != NULL && rhs == NULL)
1696 return NSOrderedDescending;
1697 else if (lhs != NULL && rhs != NULL) {
1698 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
1699 if (result != NSOrderedSame)
1700 return result;
1701 }
1702
1703 return NSOrderedSame;
1704 }
1705
1706 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1707 NSString *lhs = [self section];
1708 NSString *rhs = [package section];
1709
1710 if (lhs == NULL && rhs != NULL)
1711 return NSOrderedAscending;
1712 else if (lhs != NULL && rhs == NULL)
1713 return NSOrderedDescending;
1714 else if (lhs != NULL && rhs != NULL) {
1715 NSComparisonResult result = [lhs compare:rhs];
1716 if (result != NSOrderedSame)
1717 return result;
1718 }
1719
1720 return [self compareByName:package];
1721 }
1722
1723 - (NSComparisonResult) compareForChanges:(Package *)package {
1724 BOOL lhs = [self upgradableAndEssential:YES];
1725 BOOL rhs = [package upgradableAndEssential:YES];
1726
1727 if (lhs != rhs)
1728 return lhs ? NSOrderedAscending : NSOrderedDescending;
1729 else if (!lhs) {
1730 switch ([[self seen] compare:[package seen]]) {
1731 case NSOrderedAscending:
1732 return NSOrderedDescending;
1733 case NSOrderedSame:
1734 break;
1735 case NSOrderedDescending:
1736 return NSOrderedAscending;
1737 default:
1738 _assert(false);
1739 }
1740 }
1741
1742 return [self compareByName:package];
1743 }
1744
1745 - (void) install {
1746 pkgProblemResolver *resolver = [database_ resolver];
1747 resolver->Clear(iterator_);
1748 resolver->Protect(iterator_);
1749 pkgCacheFile &cache([database_ cache]);
1750 cache->MarkInstall(iterator_, false);
1751 pkgDepCache::StateCache &state((*cache)[iterator_]);
1752 if (!state.Install())
1753 cache->SetReInstall(iterator_, true);
1754 }
1755
1756 - (void) remove {
1757 pkgProblemResolver *resolver = [database_ resolver];
1758 resolver->Clear(iterator_);
1759 resolver->Protect(iterator_);
1760 resolver->Remove(iterator_);
1761 [database_ cache]->MarkDelete(iterator_, true);
1762 }
1763
1764 - (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
1765 return [NSNumber numberWithBool:(
1766 [self unfiltered] && [self matches:search]
1767 )];
1768 }
1769
1770 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1771 return [NSNumber numberWithBool:(
1772 (![number boolValue] || [self visible]) && [self installed] != nil
1773 )];
1774 }
1775
1776 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1777 NSString *section = [self section];
1778
1779 return [NSNumber numberWithBool:(
1780 [self visible] &&
1781 [self installed] == nil && (
1782 name == nil ||
1783 section == nil && [name length] == 0 ||
1784 [name isEqualToString:section]
1785 )
1786 )];
1787 }
1788
1789 - (NSNumber *) isVisibleInSource:(Source *)source {
1790 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1791 }
1792
1793 @end
1794 /* }}} */
1795 /* Section Class {{{ */
1796 @interface Section : NSObject {
1797 NSString *name_;
1798 size_t row_;
1799 size_t count_;
1800 }
1801
1802 - (NSComparisonResult) compareByName:(Section *)section;
1803 - (Section *) initWithName:(NSString *)name;
1804 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1805 - (NSString *) name;
1806 - (size_t) row;
1807 - (size_t) count;
1808 - (void) addToCount;
1809
1810 @end
1811
1812 @implementation Section
1813
1814 - (void) dealloc {
1815 [name_ release];
1816 [super dealloc];
1817 }
1818
1819 - (NSComparisonResult) compareByName:(Section *)section {
1820 NSString *lhs = [self name];
1821 NSString *rhs = [section name];
1822
1823 if ([lhs length] != 0 && [rhs length] != 0) {
1824 unichar lhc = [lhs characterAtIndex:0];
1825 unichar rhc = [rhs characterAtIndex:0];
1826
1827 if (isalpha(lhc) && !isalpha(rhc))
1828 return NSOrderedAscending;
1829 else if (!isalpha(lhc) && isalpha(rhc))
1830 return NSOrderedDescending;
1831 }
1832
1833 return [lhs compare:rhs options:CompareOptions_];
1834 }
1835
1836 - (Section *) initWithName:(NSString *)name {
1837 return [self initWithName:name row:0];
1838 }
1839
1840 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1841 if ((self = [super init]) != nil) {
1842 name_ = [name retain];
1843 row_ = row;
1844 } return self;
1845 }
1846
1847 - (NSString *) name {
1848 return name_;
1849 }
1850
1851 - (size_t) row {
1852 return row_;
1853 }
1854
1855 - (size_t) count {
1856 return count_;
1857 }
1858
1859 - (void) addToCount {
1860 ++count_;
1861 }
1862
1863 @end
1864 /* }}} */
1865
1866 static int Finish_;
1867 static NSArray *Finishes_;
1868
1869 /* Database Implementation {{{ */
1870 @implementation Database
1871
1872 + (Database *) sharedInstance {
1873 static Database *instance;
1874 if (instance == nil)
1875 instance = [[Database alloc] init];
1876 return instance;
1877 }
1878
1879 - (void) dealloc {
1880 _assert(false);
1881 [super dealloc];
1882 }
1883
1884 - (void) _readCydia:(NSNumber *)fd { _pooled
1885 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1886 std::istream is(&ib);
1887 std::string line;
1888
1889 static Pcre finish_r("^finish:([^:]*)$");
1890
1891 while (std::getline(is, line)) {
1892 const char *data(line.c_str());
1893 size_t size = line.size();
1894 lprintf("C:%s\n", data);
1895
1896 if (finish_r(data, size)) {
1897 NSString *finish = finish_r[1];
1898 int index = [Finishes_ indexOfObject:finish];
1899 if (index != INT_MAX && index > Finish_)
1900 Finish_ = index;
1901 }
1902 }
1903
1904 _assert(false);
1905 }
1906
1907 - (void) _readStatus:(NSNumber *)fd { _pooled
1908 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1909 std::istream is(&ib);
1910 std::string line;
1911
1912 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1913 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1914
1915 while (std::getline(is, line)) {
1916 const char *data(line.c_str());
1917 size_t size = line.size();
1918 lprintf("S:%s\n", data);
1919
1920 if (conffile_r(data, size)) {
1921 [delegate_ setConfigurationData:conffile_r[1]];
1922 } else if (strncmp(data, "status: ", 8) == 0) {
1923 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1924 [delegate_ setProgressTitle:string];
1925 } else if (pmstatus_r(data, size)) {
1926 std::string type([pmstatus_r[1] UTF8String]);
1927 NSString *id = pmstatus_r[2];
1928
1929 float percent([pmstatus_r[3] floatValue]);
1930 [delegate_ setProgressPercent:(percent / 100)];
1931
1932 NSString *string = pmstatus_r[4];
1933
1934 if (type == "pmerror")
1935 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1936 withObject:[NSArray arrayWithObjects:string, id, nil]
1937 waitUntilDone:YES
1938 ];
1939 else if (type == "pmstatus")
1940 [delegate_ setProgressTitle:string];
1941 else if (type == "pmconffile")
1942 [delegate_ setConfigurationData:string];
1943 else _assert(false);
1944 } else _assert(false);
1945 }
1946
1947 _assert(false);
1948 }
1949
1950 - (void) _readOutput:(NSNumber *)fd { _pooled
1951 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1952 std::istream is(&ib);
1953 std::string line;
1954
1955 while (std::getline(is, line)) {
1956 lprintf("O:%s\n", line.c_str());
1957 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1958 }
1959
1960 _assert(false);
1961 }
1962
1963 - (FILE *) input {
1964 return input_;
1965 }
1966
1967 - (Package *) packageWithName:(NSString *)name {
1968 if (static_cast<pkgDepCache *>(cache_) == NULL)
1969 return nil;
1970 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1971 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1972 }
1973
1974 - (Database *) init {
1975 if ((self = [super init]) != nil) {
1976 policy_ = NULL;
1977 records_ = NULL;
1978 resolver_ = NULL;
1979 fetcher_ = NULL;
1980 lock_ = NULL;
1981
1982 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1983 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1984
1985 int fds[2];
1986
1987 _assert(pipe(fds) != -1);
1988 cydiafd_ = fds[1];
1989
1990 _config->Set("APT::Keep-Fds::", cydiafd_);
1991 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
1992
1993 [NSThread
1994 detachNewThreadSelector:@selector(_readCydia:)
1995 toTarget:self
1996 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1997 ];
1998
1999 _assert(pipe(fds) != -1);
2000 statusfd_ = fds[1];
2001
2002 [NSThread
2003 detachNewThreadSelector:@selector(_readStatus:)
2004 toTarget:self
2005 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2006 ];
2007
2008 _assert(pipe(fds) != -1);
2009 _assert(dup2(fds[0], 0) != -1);
2010 _assert(close(fds[0]) != -1);
2011
2012 input_ = fdopen(fds[1], "a");
2013
2014 _assert(pipe(fds) != -1);
2015 _assert(dup2(fds[1], 1) != -1);
2016 _assert(close(fds[1]) != -1);
2017
2018 [NSThread
2019 detachNewThreadSelector:@selector(_readOutput:)
2020 toTarget:self
2021 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2022 ];
2023 } return self;
2024 }
2025
2026 - (pkgCacheFile &) cache {
2027 return cache_;
2028 }
2029
2030 - (pkgDepCache::Policy *) policy {
2031 return policy_;
2032 }
2033
2034 - (pkgRecords *) records {
2035 return records_;
2036 }
2037
2038 - (pkgProblemResolver *) resolver {
2039 return resolver_;
2040 }
2041
2042 - (pkgAcquire &) fetcher {
2043 return *fetcher_;
2044 }
2045
2046 - (NSArray *) packages {
2047 return packages_;
2048 }
2049
2050 - (NSArray *) sources {
2051 return [sources_ allValues];
2052 }
2053
2054 - (NSArray *) issues {
2055 if (cache_->BrokenCount() == 0)
2056 return nil;
2057
2058 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2059
2060 for (Package *package in packages_) {
2061 if (![package broken])
2062 continue;
2063 pkgCache::PkgIterator pkg([package iterator]);
2064
2065 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2066 [entry addObject:[package name]];
2067 [issues addObject:entry];
2068
2069 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2070 if (ver.end())
2071 continue;
2072
2073 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2074 pkgCache::DepIterator start;
2075 pkgCache::DepIterator end;
2076 dep.GlobOr(start, end); // ++dep
2077
2078 if (!cache_->IsImportantDep(end))
2079 continue;
2080 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2081 continue;
2082
2083 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2084 [entry addObject:failure];
2085 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2086
2087 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2088 [failure addObject:[package name]];
2089
2090 pkgCache::PkgIterator target(start.TargetPkg());
2091 if (target->ProvidesList != 0)
2092 [failure addObject:@"?"];
2093 else {
2094 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2095 if (!ver.end())
2096 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2097 else if (!cache_[target].CandidateVerIter(cache_).end())
2098 [failure addObject:@"-"];
2099 else if (target->ProvidesList == 0)
2100 [failure addObject:@"!"];
2101 else
2102 [failure addObject:@"%"];
2103 }
2104
2105 _forever {
2106 if (start.TargetVer() != 0)
2107 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2108 if (start == end)
2109 break;
2110 ++start;
2111 }
2112 }
2113 }
2114
2115 return issues;
2116 }
2117
2118 - (void) reloadData {
2119 _error->Discard();
2120
2121 delete list_;
2122 list_ = NULL;
2123 manager_ = NULL;
2124 delete lock_;
2125 lock_ = NULL;
2126 delete fetcher_;
2127 fetcher_ = NULL;
2128 delete resolver_;
2129 resolver_ = NULL;
2130 delete records_;
2131 records_ = NULL;
2132 delete policy_;
2133 policy_ = NULL;
2134
2135 cache_.Close();
2136
2137 if (!cache_.Open(progress_, true)) {
2138 std::string error;
2139 if (!_error->PopMessage(error))
2140 _assert(false);
2141 _error->Discard();
2142 lprintf("cache_.Open():[%s]\n", error.c_str());
2143
2144 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2145 [delegate_ repairWithSelector:@selector(configure)];
2146 else if (error == "The package lists or status file could not be parsed or opened.")
2147 [delegate_ repairWithSelector:@selector(update)];
2148 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2149 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2150 // else if (error == "The list of sources could not be read.")
2151 else _assert(false);
2152
2153 return;
2154 }
2155
2156 now_ = [[NSDate date] retain];
2157
2158 policy_ = new pkgDepCache::Policy();
2159 records_ = new pkgRecords(cache_);
2160 resolver_ = new pkgProblemResolver(cache_);
2161 fetcher_ = new pkgAcquire(&status_);
2162 lock_ = NULL;
2163
2164 list_ = new pkgSourceList();
2165 _assert(list_->ReadMainList());
2166
2167 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2168 _assert(pkgApplyStatus(cache_));
2169
2170 if (cache_->BrokenCount() != 0) {
2171 _assert(pkgFixBroken(cache_));
2172 _assert(cache_->BrokenCount() == 0);
2173 _assert(pkgMinimizeUpgrade(cache_));
2174 }
2175
2176 [sources_ removeAllObjects];
2177 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2178 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2179 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2180 [sources_
2181 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2182 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2183 ];
2184 }
2185
2186 [packages_ removeAllObjects];
2187 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
2188 if (Package *package = [Package packageWithIterator:iterator database:self])
2189 [packages_ addObject:package];
2190
2191 [packages_ sortUsingSelector:@selector(compareByName:)];
2192 }
2193
2194 - (void) configure {
2195 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2196 system([dpkg UTF8String]);
2197 }
2198
2199 - (void) clean {
2200 if (lock_ != NULL)
2201 return;
2202
2203 FileFd Lock;
2204 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2205 _assert(!_error->PendingError());
2206
2207 pkgAcquire fetcher;
2208 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2209
2210 class LogCleaner :
2211 public pkgArchiveCleaner
2212 {
2213 protected:
2214 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
2215 unlink(File);
2216 }
2217 } cleaner;
2218
2219 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2220 std::string error;
2221 while (_error->PopMessage(error))
2222 lprintf("ArchiveCleaner: %s\n", error.c_str());
2223 }
2224 }
2225
2226 - (void) prepare {
2227 pkgRecords records(cache_);
2228
2229 lock_ = new FileFd();
2230 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2231 _assert(!_error->PendingError());
2232
2233 pkgSourceList list;
2234 // XXX: explain this with an error message
2235 _assert(list.ReadMainList());
2236
2237 manager_ = (_system->CreatePM(cache_));
2238 _assert(manager_->GetArchives(fetcher_, &list, &records));
2239 _assert(!_error->PendingError());
2240 }
2241
2242 - (void) perform {
2243 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2244 pkgSourceList list;
2245 _assert(list.ReadMainList());
2246 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2247 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2248 }
2249
2250 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2251 _trace();
2252 return;
2253 }
2254
2255 bool failed = false;
2256 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2257 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2258 continue;
2259
2260 std::string uri = (*item)->DescURI();
2261 std::string error = (*item)->ErrorText;
2262
2263 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2264 failed = true;
2265
2266 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2267 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2268 waitUntilDone:YES
2269 ];
2270 }
2271
2272 if (failed) {
2273 _trace();
2274 return;
2275 }
2276
2277 _system->UnLock();
2278 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2279
2280 if (_error->PendingError()) {
2281 _trace();
2282 return;
2283 }
2284
2285 if (result == pkgPackageManager::Failed) {
2286 _trace();
2287 return;
2288 }
2289
2290 if (result != pkgPackageManager::Completed) {
2291 _trace();
2292 return;
2293 }
2294
2295 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2296 pkgSourceList list;
2297 _assert(list.ReadMainList());
2298 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2299 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2300 }
2301
2302 if (![before isEqualToArray:after])
2303 [self update];
2304 }
2305
2306 - (void) upgrade {
2307 _assert(pkgDistUpgrade(cache_));
2308 }
2309
2310 - (void) update {
2311 [self updateWithStatus:status_];
2312 }
2313
2314 - (void) updateWithStatus:(Status &)status {
2315 pkgSourceList list;
2316 _assert(list.ReadMainList());
2317
2318 FileFd lock;
2319 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2320 _assert(!_error->PendingError());
2321
2322 pkgAcquire fetcher(&status);
2323 _assert(list.GetIndexes(&fetcher));
2324
2325 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2326 bool failed = false;
2327 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2328 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2329 (*item)->Finished();
2330 failed = true;
2331 }
2332
2333 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2334 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2335 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2336 }
2337
2338 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2339 Changed_ = true;
2340 }
2341 }
2342
2343 - (void) setDelegate:(id)delegate {
2344 delegate_ = delegate;
2345 status_.setDelegate(delegate);
2346 progress_.setDelegate(delegate);
2347 }
2348
2349 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2350 pkgIndexFile *index(NULL);
2351 list_->FindIndex(file, index);
2352 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2353 }
2354
2355 @end
2356 /* }}} */
2357
2358 /* PopUp Windows {{{ */
2359 @interface PopUpView : UIView {
2360 _transient id delegate_;
2361 UITransitionView *transition_;
2362 UIView *overlay_;
2363 }
2364
2365 - (void) cancel;
2366 - (id) initWithView:(UIView *)view delegate:(id)delegate;
2367
2368 @end
2369
2370 @implementation PopUpView
2371
2372 - (void) dealloc {
2373 [transition_ setDelegate:nil];
2374 [transition_ release];
2375 [overlay_ release];
2376 [super dealloc];
2377 }
2378
2379 - (void) cancel {
2380 [transition_ transition:UITransitionPushFromTop toView:nil];
2381 }
2382
2383 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2384 if (from != nil && to == nil)
2385 [self removeFromSuperview];
2386 }
2387
2388 - (id) initWithView:(UIView *)view delegate:(id)delegate {
2389 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2390 delegate_ = delegate;
2391
2392 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2393 [self addSubview:transition_];
2394
2395 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2396
2397 [view addSubview:self];
2398
2399 [transition_ setDelegate:self];
2400
2401 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2402 [transition_ transition:UITransitionNone toView:blank];
2403 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2404 } return self;
2405 }
2406
2407 @end
2408 /* }}} */
2409
2410 /* Mail Composition {{{ */
2411 @interface MailToView : PopUpView {
2412 MailComposeController *controller_;
2413 }
2414
2415 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2416
2417 @end
2418
2419 @implementation MailToView
2420
2421 - (void) dealloc {
2422 [controller_ release];
2423 [super dealloc];
2424 }
2425
2426 - (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2427 NSLog(@"will");
2428 }
2429
2430 - (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2431 NSLog(@"did:%@", delivery);
2432 // [UIApp setStatusBarShowsProgress:NO];
2433 if ([controller error]){
2434 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2435 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2436 [mailAlertSheet setBodyText:[controller error]];
2437 [mailAlertSheet popupAlertAnimated:YES];
2438 }
2439 }
2440
2441 - (void) showError {
2442 NSLog(@"%@", [controller_ error]);
2443 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2444 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2445 [mailAlertSheet setBodyText:[controller_ error]];
2446 [mailAlertSheet popupAlertAnimated:YES];
2447 }
2448
2449 - (void) deliverMessage { _pooled
2450 if (![controller_ deliverMessage])
2451 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2452 }
2453
2454 - (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
2455 if ([controller_ needsDelivery])
2456 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
2457 else
2458 [self cancel];
2459 }
2460
2461 - (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2462 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2463 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2464 [controller_ setDelegate:self];
2465 [controller_ initializeUI];
2466 [controller_ setupForURL:url];
2467
2468 UIView *view([controller_ view]);
2469 [overlay_ addSubview:view];
2470 } return self;
2471 }
2472
2473 @end
2474 /* }}} */
2475 /* Confirmation View {{{ */
2476 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2477 if (!iterator.end())
2478 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2479 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2480 continue;
2481 pkgCache::PkgIterator package(dep.TargetPkg());
2482 if (package.end())
2483 continue;
2484 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2485 return true;
2486 }
2487
2488 return false;
2489 }
2490
2491 @protocol ConfirmationViewDelegate
2492 - (void) cancel;
2493 - (void) confirm;
2494 @end
2495
2496 @interface ConfirmationView : BrowserView {
2497 _transient Database *database_;
2498 UIActionSheet *essential_;
2499 NSArray *changes_;
2500 NSArray *issues_;
2501 NSArray *sizes_;
2502 BOOL substrate_;
2503 }
2504
2505 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2506
2507 @end
2508
2509 @implementation ConfirmationView
2510
2511 - (void) dealloc {
2512 [changes_ release];
2513 if (issues_ != nil)
2514 [issues_ release];
2515 [sizes_ release];
2516 if (essential_ != nil)
2517 [essential_ release];
2518 [super dealloc];
2519 }
2520
2521 - (void) cancel {
2522 [delegate_ cancel];
2523 [book_ popFromSuperviewAnimated:YES];
2524 }
2525
2526 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
2527 NSString *context = [sheet context];
2528
2529 if ([context isEqualToString:@"remove"])
2530 switch (button) {
2531 case 1:
2532 [self cancel];
2533 break;
2534 case 2:
2535 if (substrate_)
2536 Finish_ = 2;
2537 [delegate_ confirm];
2538 break;
2539 default:
2540 _assert(false);
2541 }
2542 else if ([context isEqualToString:@"unable"])
2543 [self cancel];
2544
2545 [sheet dismiss];
2546 }
2547
2548 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2549 [window setValue:changes_ forKey:@"changes"];
2550 [window setValue:issues_ forKey:@"issues"];
2551 [window setValue:sizes_ forKey:@"sizes"];
2552 [super webView:sender didClearWindowObject:window forFrame:frame];
2553 }
2554
2555 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2556 if ((self = [super initWithBook:book]) != nil) {
2557 database_ = database;
2558
2559 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2560 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2561 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2562 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2563 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2564
2565 bool remove(false);
2566
2567 pkgDepCache::Policy *policy([database_ policy]);
2568
2569 pkgCacheFile &cache([database_ cache]);
2570 NSArray *packages = [database_ packages];
2571 for (size_t i(0), e = [packages count]; i != e; ++i) {
2572 Package *package = [packages objectAtIndex:i];
2573 pkgCache::PkgIterator iterator = [package iterator];
2574 pkgDepCache::StateCache &state(cache[iterator]);
2575
2576 NSString *name([package name]);
2577
2578 if (state.NewInstall())
2579 [installing addObject:name];
2580 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2581 [reinstalling addObject:name];
2582 else if (state.Upgrade())
2583 [upgrading addObject:name];
2584 else if (state.Downgrade())
2585 [downgrading addObject:name];
2586 else if (state.Delete()) {
2587 if ([package essential])
2588 remove = true;
2589 [removing addObject:name];
2590 } else continue;
2591
2592 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2593 substrate_ |= DepSubstrate(iterator.CurrentVer());
2594 }
2595
2596 if (!remove)
2597 essential_ = nil;
2598 else if (Advanced_ || true) {
2599 essential_ = [[UIActionSheet alloc]
2600 initWithTitle:@"Removing Essentials"
2601 buttons:[NSArray arrayWithObjects:
2602 @"Cancel Operation (Safe)",
2603 @"Force Removal (Unsafe)",
2604 nil]
2605 defaultButtonIndex:0
2606 delegate:self
2607 context:@"remove"
2608 ];
2609
2610 #ifndef __OBJC2__
2611 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2612 #endif
2613 [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."];
2614 } else {
2615 essential_ = [[UIActionSheet alloc]
2616 initWithTitle:@"Unable to Comply"
2617 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2618 defaultButtonIndex:0
2619 delegate:self
2620 context:@"unable"
2621 ];
2622
2623 [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."];
2624 }
2625
2626 changes_ = [[NSArray alloc] initWithObjects:
2627 installing,
2628 reinstalling,
2629 upgrading,
2630 downgrading,
2631 removing,
2632 nil];
2633
2634 issues_ = [database_ issues];
2635 if (issues_ != nil)
2636 issues_ = [issues_ retain];
2637
2638 sizes_ = [[NSArray alloc] initWithObjects:
2639 SizeString([database_ fetcher].FetchNeeded()),
2640 SizeString([database_ fetcher].PartialPresent()),
2641 SizeString([database_ cache]->UsrSize()),
2642 nil];
2643
2644 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
2645 } return self;
2646 }
2647
2648 // XXX: replace with <title/>
2649 - (NSString *) title {
2650 return issues_ == nil ? @"Confirm Changes" : @"Cannot Comply";
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 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6659 }
6660
6661 - (RVPage *) pageForPackage:(NSString *)name {
6662 if (Package *package = [database_ packageWithName:name]) {
6663 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6664 [view setPackage:package];
6665 return view;
6666 } else {
6667 UIActionSheet *sheet = [[[UIActionSheet alloc]
6668 initWithTitle:@"Cannot Locate Package"
6669 buttons:[NSArray arrayWithObjects:@"Close", nil]
6670 defaultButtonIndex:0
6671 delegate:self
6672 context:@"missing"
6673 ] autorelease];
6674
6675 [sheet setBodyText:[NSString stringWithFormat:
6676 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6677 , name]];
6678
6679 [sheet popupAlertAnimated:YES];
6680 return nil;
6681 }
6682 }
6683
6684 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6685 NSString *href = [url absoluteString];
6686
6687 if (tag != NULL)
6688 tag = 0;
6689
6690 if ([href isEqualToString:@"cydia://add-source"])
6691 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6692 else if ([href isEqualToString:@"cydia://sources"])
6693 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6694 else if ([href isEqualToString:@"cydia://packages"])
6695 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6696 else if ([href hasPrefix:@"cydia://url/"])
6697 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6698 else if ([href hasPrefix:@"cydia://launch/"])
6699 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6700 else if ([href hasPrefix:@"cydia://package-signature/"])
6701 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6702 else if ([href hasPrefix:@"cydia://package/"])
6703 return [self pageForPackage:[href substringFromIndex:16]];
6704 else if ([href hasPrefix:@"cydia://files/"]) {
6705 NSString *name = [href substringFromIndex:14];
6706
6707 if (Package *package = [database_ packageWithName:name]) {
6708 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6709 [files setPackage:package];
6710 return files;
6711 }
6712 }
6713
6714 return nil;
6715 }
6716
6717 - (void) applicationOpenURL:(NSURL *)url {
6718 [super applicationOpenURL:url];
6719 int tag;
6720 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6721 [self setPage:page];
6722 [buttonbar_ showSelectionForButton:tag];
6723 tag_ = tag;
6724 }
6725 }
6726
6727 - (void) applicationDidFinishLaunching:(id)unused {
6728 Font12_ = [[UIFont systemFontOfSize:12] retain];
6729 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6730 Font14_ = [[UIFont systemFontOfSize:14] retain];
6731 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6732 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6733
6734 _assert(pkgInitConfig(*_config));
6735 _assert(pkgInitSystem(*_config, _system));
6736
6737 tag_ = 1;
6738
6739 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6740 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6741
6742 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6743
6744 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6745 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6746
6747 [window_ orderFront:self];
6748 [window_ makeKey:self];
6749 [window_ setHidden:NO];
6750
6751 database_ = [Database sharedInstance];
6752 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6753 [database_ setDelegate:progress_];
6754 [window_ setContentView:progress_];
6755
6756 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6757 [progress_ setContentView:underlay_];
6758
6759 [progress_ resetView];
6760
6761 if (
6762 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6763 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6764 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6765 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6766 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6767 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6768 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6769 ) {
6770 [self setIdleTimerDisabled:YES];
6771
6772 hud_ = [self addProgressHUD];
6773 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6774
6775 [self setStatusBarShowsProgress:YES];
6776
6777 [NSThread
6778 detachNewThreadSelector:@selector(reorganize)
6779 toTarget:self
6780 withObject:nil
6781 ];
6782 } else
6783 [self finish];
6784 }
6785
6786 /* Web Scripting {{{ */
6787 + (NSString *) webScriptNameForSelector:(SEL)selector {
6788 if (selector == @selector(supports:))
6789 return @"supports";
6790 return nil;
6791 }
6792
6793 + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
6794 return selector != @selector(supports:);
6795 }
6796
6797 - (BOOL) supports:(NSString *)feature {
6798 return [feature isEqualToString:@"window.open"];
6799 }
6800 /* }}} */
6801
6802 - (void) showKeyboard:(BOOL)show {
6803 CGSize keysize = [UIKeyboard defaultSize];
6804 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6805 CGRect keyup = keydown;
6806 keyup.origin.y -= keysize.height;
6807
6808 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6809 [animation setSignificantRectFields:2];
6810
6811 if (show) {
6812 [animation setStartFrame:keydown];
6813 [animation setEndFrame:keyup];
6814 [keyboard_ activate];
6815 } else {
6816 [animation setStartFrame:keyup];
6817 [animation setEndFrame:keydown];
6818 [keyboard_ deactivate];
6819 }
6820
6821 [[UIAnimator sharedAnimator]
6822 addAnimations:[NSArray arrayWithObjects:animation, nil]
6823 withDuration:KeyboardTime_
6824 start:YES
6825 ];
6826 }
6827
6828 - (void) slideUp:(UIActionSheet *)alert {
6829 if (Advanced_)
6830 [alert presentSheetFromButtonBar:buttonbar_];
6831 else
6832 [alert presentSheetInView:overlay_];
6833 }
6834
6835 @end
6836
6837 void AddPreferences(NSString *plist) { _pooled
6838 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6839 _assert(settings != NULL);
6840 NSMutableArray *items = [settings objectForKey:@"items"];
6841
6842 bool cydia(false);
6843
6844 for (size_t i(0); i != [items count]; ++i) {
6845 NSMutableDictionary *item([items objectAtIndex:i]);
6846 NSString *label = [item objectForKey:@"label"];
6847 if (label != nil && [label isEqualToString:@"Cydia"]) {
6848 cydia = true;
6849 break;
6850 }
6851 }
6852
6853 if (!cydia) {
6854 for (size_t i(0); i != [items count]; ++i) {
6855 NSDictionary *item([items objectAtIndex:i]);
6856 NSString *label = [item objectForKey:@"label"];
6857 if (label != nil && [label isEqualToString:@"General"]) {
6858 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6859 @"CydiaSettings", @"bundle",
6860 @"PSLinkCell", @"cell",
6861 [NSNumber numberWithBool:YES], @"hasIcon",
6862 [NSNumber numberWithBool:YES], @"isController",
6863 @"Cydia", @"label",
6864 nil] atIndex:(i + 1)];
6865
6866 break;
6867 }
6868 }
6869
6870 _assert([settings writeToFile:plist atomically:YES] == YES);
6871 }
6872 }
6873
6874 /*IMP alloc_;
6875 id Alloc_(id self, SEL selector) {
6876 id object = alloc_(self, selector);
6877 lprintf("[%s]A-%p\n", self->isa->name, object);
6878 return object;
6879 }*/
6880
6881 /*IMP dealloc_;
6882 id Dealloc_(id self, SEL selector) {
6883 id object = dealloc_(self, selector);
6884 lprintf("[%s]D-%p\n", self->isa->name, object);
6885 return object;
6886 }*/
6887
6888 int main(int argc, char *argv[]) { _pooled
6889 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6890
6891 App_ = [[NSBundle mainBundle] bundlePath];
6892 Home_ = NSHomeDirectory();
6893 Locale_ = CFLocaleCopyCurrent();
6894
6895 {
6896 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6897 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6898 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6899 Sounds_Keyboard_ = [keyboard boolValue];
6900 }
6901
6902 setuid(0);
6903 setgid(0);
6904
6905 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6906 _assert(errno == ENOENT);
6907 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6908 _assert(errno == ENOENT);
6909
6910 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6911 alloc_ = alloc->method_imp;
6912 alloc->method_imp = (IMP) &Alloc_;*/
6913
6914 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6915 dealloc_ = dealloc->method_imp;
6916 dealloc->method_imp = (IMP) &Dealloc_;*/
6917
6918 size_t size;
6919
6920 int maxproc;
6921 size = sizeof(maxproc);
6922 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6923 perror("sysctlbyname(\"kern.maxproc\", ?)");
6924 else if (maxproc < 64) {
6925 maxproc = 64;
6926 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6927 perror("sysctlbyname(\"kern.maxproc\", #)");
6928 }
6929
6930 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6931 char *machine = new char[size];
6932 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6933 perror("sysctlbyname(\"hw.machine\", ?)");
6934 else
6935 Machine_ = machine;
6936
6937 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6938
6939 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6940 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6941
6942 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6943 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6944 else {
6945 Settings_ = [Metadata_ objectForKey:@"Settings"];
6946
6947 Packages_ = [Metadata_ objectForKey:@"Packages"];
6948 Sections_ = [Metadata_ objectForKey:@"Sections"];
6949 Sources_ = [Metadata_ objectForKey:@"Sources"];
6950 }
6951
6952 if (Settings_ != nil)
6953 Role_ = [Settings_ objectForKey:@"Role"];
6954
6955 if (Packages_ == nil) {
6956 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6957 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6958 }
6959
6960 if (Sections_ == nil) {
6961 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6962 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6963 }
6964
6965 if (Sources_ == nil) {
6966 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6967 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6968 }
6969
6970 #if RecycleWebViews
6971 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
6972 #endif
6973
6974 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
6975 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
6976
6977 if (access("/User", F_OK) != 0)
6978 system("/usr/libexec/cydia/firmware.sh");
6979
6980 _assert([[NSFileManager defaultManager]
6981 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
6982 withIntermediateDirectories:YES
6983 attributes:nil
6984 error:NULL
6985 ]);
6986
6987 space_ = CGColorSpaceCreateDeviceRGB();
6988
6989 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6990 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6991 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6992 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
6993 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6994 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6995
6996 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
6997
6998 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6999
7000 UIApplicationUseLegacyEvents(YES);
7001 UIKeyboardDisableAutomaticAppearance();
7002
7003 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7004
7005 CGColorSpaceRelease(space_);
7006 CFRelease(Locale_);
7007
7008 return value;
7009 }