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