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