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