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