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