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