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