]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Minor documentation additions.
[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 @interface PackageView : RVPage {
3152 _transient Database *database_;
3153 UIPreferencesTable *table_;
3154 Package *package_;
3155 NSString *name_;
3156 UITextView *description_;
3157 NSMutableArray *buttons_;
3158 }
3159
3160 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3161 - (void) setPackage:(Package *)package;
3162
3163 @end
3164
3165 @implementation PackageView
3166
3167 - (void) dealloc {
3168 [table_ setDataSource:nil];
3169 [table_ setDelegate:nil];
3170
3171 if (package_ != nil)
3172 [package_ release];
3173 if (name_ != nil)
3174 [name_ release];
3175 if (description_ != nil)
3176 [description_ release];
3177 [table_ release];
3178 [buttons_ release];
3179 [super dealloc];
3180 }
3181
3182 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
3183 int number = 2;
3184 if ([package_ installed] != nil)
3185 ++number;
3186 if ([package_ source] != nil)
3187 ++number;
3188 return number;
3189 }
3190
3191 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
3192 if (group-- == 0)
3193 return nil;
3194 else if ([package_ installed] != nil && group-- == 0)
3195 return @"Installed Package";
3196 else if (group-- == 0)
3197 return @"Package Details";
3198 else if ([package_ source] != nil && group-- == 0)
3199 return @"Source Information";
3200 else _assert(false);
3201 }
3202
3203 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
3204 if (description_ == nil || group != 0 || row != ([package_ author] == nil ? 1 : 2))
3205 return proposed;
3206 else
3207 return [description_ visibleTextRect].size.height + TextViewOffset_;
3208 }
3209
3210 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3211 if (group-- == 0) {
3212 int number = 1;
3213 if ([package_ author] != nil)
3214 ++number;
3215 if (description_ != nil)
3216 ++number;
3217 if ([package_ website] != nil)
3218 ++number;
3219 return number;
3220 } else if ([package_ installed] != nil && group-- == 0)
3221 return 2;
3222 else if (group-- == 0) {
3223 int number = 2;
3224 if ([package_ size] != 0)
3225 ++number;
3226 if ([package_ maintainer] != nil)
3227 ++number;
3228 if ([package_ sponsor] != nil)
3229 ++number;
3230 if ([package_ relationships] != nil)
3231 ++number;
3232 if ([[package_ source] trusted])
3233 ++number;
3234 return number;
3235 } else if ([package_ source] != nil && group-- == 0) {
3236 Source *source = [package_ source];
3237 NSString *description = [source description];
3238 int number = 1;
3239 if (description != nil && ![description isEqualToString:[source label]])
3240 ++number;
3241 if ([source origin] != nil)
3242 ++number;
3243 return number;
3244 } else _assert(false);
3245 }
3246
3247 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3248 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3249 [cell setShowSelection:NO];
3250
3251 if (group-- == 0) {
3252 if (false) {
3253 } else if (row-- == 0) {
3254 [cell setTitle:[package_ name]];
3255 [cell setValue:[package_ latest]];
3256 } else if ([package_ author] != nil && row-- == 0) {
3257 [cell setTitle:@"Author"];
3258 [cell setValue:[[package_ author] name]];
3259 [cell setShowDisclosure:YES];
3260 [cell setShowSelection:YES];
3261 } else if (description_ != nil && row-- == 0) {
3262 [cell addSubview:description_];
3263 } else if ([package_ website] != nil && row-- == 0) {
3264 [cell setTitle:@"More Information"];
3265 [cell setShowDisclosure:YES];
3266 [cell setShowSelection:YES];
3267 } else _assert(false);
3268 } else if ([package_ installed] != nil && group-- == 0) {
3269 if (false) {
3270 } else if (row-- == 0) {
3271 [cell setTitle:@"Version"];
3272 NSString *installed([package_ installed]);
3273 [cell setValue:(installed == nil ? @"n/a" : installed)];
3274 } else if (row-- == 0) {
3275 [cell setTitle:@"Filesystem Content"];
3276 [cell setShowDisclosure:YES];
3277 [cell setShowSelection:YES];
3278 } else _assert(false);
3279 } else if (group-- == 0) {
3280 if (false) {
3281 } else if (row-- == 0) {
3282 [cell setTitle:@"Identifier"];
3283 [cell setValue:[package_ id]];
3284 } else if (row-- == 0) {
3285 [cell setTitle:@"Section"];
3286 NSString *section([package_ section]);
3287 [cell setValue:(section == nil ? @"n/a" : section)];
3288 } else if ([package_ size] != 0 && row-- == 0) {
3289 [cell setTitle:@"Expanded Size"];
3290 [cell setValue:SizeString([package_ size])];
3291 } else if ([package_ maintainer] != nil && row-- == 0) {
3292 [cell setTitle:@"Maintainer"];
3293 [cell setValue:[[package_ maintainer] name]];
3294 [cell setShowDisclosure:YES];
3295 [cell setShowSelection:YES];
3296 } else if ([package_ sponsor] != nil && row-- == 0) {
3297 [cell setTitle:@"Sponsor"];
3298 [cell setValue:[[package_ sponsor] name]];
3299 [cell setShowDisclosure:YES];
3300 [cell setShowSelection:YES];
3301 } else if ([package_ relationships] != nil && row-- == 0) {
3302 [cell setTitle:@"Package Relationships"];
3303 [cell setShowDisclosure:YES];
3304 [cell setShowSelection:YES];
3305 } else if ([[package_ source] trusted] && row-- == 0) {
3306 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3307 [cell setValue:@"This package has been signed."];
3308 } else _assert(false);
3309 } else if ([package_ source] != nil && group-- == 0) {
3310 Source *source = [package_ source];
3311 NSString *description = [source description];
3312
3313 if (false) {
3314 } else if (row-- == 0) {
3315 NSString *label = [source label];
3316 if (label == nil)
3317 label = [source uri];
3318 [cell setTitle:label];
3319 [cell setValue:[source version]];
3320 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3321 [cell setValue:description];
3322 } else if ([source origin] != nil && row-- == 0) {
3323 [cell setTitle:@"Origin"];
3324 [cell setValue:[source origin]];
3325 } else _assert(false);
3326 } else _assert(false);
3327
3328 return cell;
3329 }
3330
3331 - (BOOL) canSelectRow:(int)row {
3332 return YES;
3333 }
3334
3335 - (void) tableRowSelected:(NSNotification *)notification {
3336 int row = [table_ selectedRow];
3337 if (row == INT_MAX)
3338 return;
3339
3340 #define _else else goto _label; return; } _label:
3341
3342 if (true) {
3343 if (row-- == 0) {
3344 } else if (row-- == 0) {
3345 } else if ([package_ author] != nil && row-- == 0) {
3346 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3347 [[package_ author] email],
3348 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3349 [package_ name]
3350 ] stringByAddingPercentEscapes]
3351 ]]];
3352 } else if (description_ != nil && row-- == 0) {
3353 } else if ([package_ website] != nil && row-- == 0) {
3354 NSURL *url = [NSURL URLWithString:[package_ website]];
3355 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3356 [browser setDelegate:delegate_];
3357 [book_ pushPage:browser];
3358 [browser loadURL:url];
3359 } _else if ([package_ installed] != nil) {
3360 if (row-- == 0) {
3361 } else if (row-- == 0) {
3362 } else if (row-- == 0) {
3363 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3364 [files setDelegate:delegate_];
3365 [files setPackage:package_];
3366 [book_ pushPage:files];
3367 } _else if (true) {
3368 if (row-- == 0) {
3369 } else if (row-- == 0) {
3370 } else if (row-- == 0) {
3371 } else if ([package_ size] != 0 && row-- == 0) {
3372 } else if ([package_ maintainer] != nil && row-- == 0) {
3373 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3374 [[package_ maintainer] email],
3375 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3376 [package_ name]
3377 ] stringByAddingPercentEscapes]
3378 ]]];
3379 } else if ([package_ sponsor] != nil && row-- == 0) {
3380 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3381 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3382 [browser setDelegate:delegate_];
3383 [book_ pushPage:browser];
3384 [browser loadURL:url];
3385 } else if ([package_ relationships] != nil && row-- == 0) {
3386 } else if ([[package_ source] trusted] && row-- == 0) {
3387 } _else if ([package_ source] != nil) {
3388 Source *source = [package_ source];
3389 NSString *description = [source description];
3390
3391 if (row-- == 0) {
3392 } else if (row-- == 0) {
3393 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3394 } else if ([source origin] != nil && row-- == 0) {
3395 } _else _assert(false);
3396
3397 #undef _else
3398 }
3399
3400 - (void) _clickButtonWithName:(NSString *)name {
3401 if ([name isEqualToString:@"Install"])
3402 [delegate_ installPackage:package_];
3403 else if ([name isEqualToString:@"Reinstall"])
3404 [delegate_ installPackage:package_];
3405 else if ([name isEqualToString:@"Remove"])
3406 [delegate_ removePackage:package_];
3407 else if ([name isEqualToString:@"Upgrade"])
3408 [delegate_ installPackage:package_];
3409 else _assert(false);
3410 }
3411
3412 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3413 int count = [buttons_ count];
3414 _assert(count != 0);
3415 _assert(button <= count + 1);
3416
3417 if (count != button - 1)
3418 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3419
3420 [sheet dismiss];
3421 }
3422
3423 - (void) _rightButtonClicked {
3424 int count = [buttons_ count];
3425 _assert(count != 0);
3426
3427 if (count == 1)
3428 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3429 else {
3430 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3431 [buttons addObjectsFromArray:buttons_];
3432 [buttons addObject:@"Cancel"];
3433
3434 [delegate_ slideUp:[[[UIAlertSheet alloc]
3435 initWithTitle:nil
3436 buttons:buttons
3437 defaultButtonIndex:2
3438 delegate:self
3439 context:@"manage"
3440 ] autorelease]];
3441 }
3442 }
3443
3444 - (NSString *) rightButtonTitle {
3445 int count = [buttons_ count];
3446 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3447 }
3448
3449 - (NSString *) title {
3450 return @"Details";
3451 }
3452
3453 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3454 if ((self = [super initWithBook:book]) != nil) {
3455 database_ = database;
3456
3457 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3458 [self addSubview:table_];
3459
3460 [table_ setDataSource:self];
3461 [table_ setDelegate:self];
3462
3463 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3464 } return self;
3465 }
3466
3467 - (void) setPackage:(Package *)package {
3468 if (package_ != nil) {
3469 [package_ autorelease];
3470 package_ = nil;
3471 }
3472
3473 if (name_ != nil) {
3474 [name_ release];
3475 name_ = nil;
3476 }
3477
3478 if (description_ != nil) {
3479 [description_ release];
3480 description_ = nil;
3481 }
3482
3483 [buttons_ removeAllObjects];
3484
3485 if (package != nil) {
3486 package_ = [package retain];
3487 name_ = [[package id] retain];
3488
3489 NSString *description([package description]);
3490 if (description == nil)
3491 description = [package tagline];
3492 if (description != nil) {
3493 description_ = [GetTextView(description, 12, true) retain];
3494 [description_ setTextColor:Black_];
3495 }
3496
3497 [table_ reloadData];
3498
3499 if ([package_ source] == nil);
3500 else if ([package_ upgradableAndEssential:NO])
3501 [buttons_ addObject:@"Upgrade"];
3502 else if ([package_ installed] == nil)
3503 [buttons_ addObject:@"Install"];
3504 else
3505 [buttons_ addObject:@"Reinstall"];
3506 if ([package_ installed] != nil)
3507 [buttons_ addObject:@"Remove"];
3508 }
3509 }
3510
3511 - (void) resetViewAnimated:(BOOL)animated {
3512 [table_ resetViewAnimated:animated];
3513 }
3514
3515 - (void) reloadData {
3516 [self setPackage:[database_ packageWithName:name_]];
3517 [self reloadButtons];
3518 }
3519
3520 @end
3521 /* }}} */
3522 /* Package Table {{{ */
3523 @interface PackageTable : RVPage {
3524 _transient Database *database_;
3525 NSString *title_;
3526 SEL filter_;
3527 id object_;
3528 NSMutableArray *packages_;
3529 NSMutableArray *sections_;
3530 UISectionList *list_;
3531 }
3532
3533 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3534
3535 - (void) setDelegate:(id)delegate;
3536 - (void) setObject:(id)object;
3537
3538 - (void) reloadData;
3539 - (void) resetCursor;
3540
3541 - (UISectionList *) list;
3542
3543 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3544
3545 @end
3546
3547 @implementation PackageTable
3548
3549 - (void) dealloc {
3550 [list_ setDataSource:nil];
3551
3552 [title_ release];
3553 if (object_ != nil)
3554 [object_ release];
3555 [packages_ release];
3556 [sections_ release];
3557 [list_ release];
3558 [super dealloc];
3559 }
3560
3561 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3562 return [sections_ count];
3563 }
3564
3565 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3566 return [[sections_ objectAtIndex:section] name];
3567 }
3568
3569 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3570 return [[sections_ objectAtIndex:section] row];
3571 }
3572
3573 - (int) numberOfRowsInTable:(UITable *)table {
3574 return [packages_ count];
3575 }
3576
3577 - (float) table:(UITable *)table heightForRow:(int)row {
3578 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3579 }
3580
3581 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3582 if (reusing == nil)
3583 reusing = [[[PackageCell alloc] init] autorelease];
3584 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3585 return reusing;
3586 }
3587
3588 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3589 return NO;
3590 }
3591
3592 - (void) tableRowSelected:(NSNotification *)notification {
3593 int row = [[notification object] selectedRow];
3594 if (row == INT_MAX)
3595 return;
3596
3597 Package *package = [packages_ objectAtIndex:row];
3598 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3599 [view setDelegate:delegate_];
3600 [view setPackage:package];
3601 [book_ pushPage:view];
3602 }
3603
3604 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3605 if ((self = [super initWithBook:book]) != nil) {
3606 database_ = database;
3607 title_ = [title retain];
3608 filter_ = filter;
3609 object_ = object == nil ? nil : [object retain];
3610
3611 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3612 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3613
3614 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3615 [list_ setDataSource:self];
3616
3617 UITableColumn *column = [[[UITableColumn alloc]
3618 initWithTitle:@"Name"
3619 identifier:@"name"
3620 width:[self frame].size.width
3621 ] autorelease];
3622
3623 UITable *table = [list_ table];
3624 [table setSeparatorStyle:1];
3625 [table addTableColumn:column];
3626 [table setDelegate:self];
3627 [table setReusesTableCells:YES];
3628
3629 [self addSubview:list_];
3630 [self reloadData];
3631 } return self;
3632 }
3633
3634 - (void) setDelegate:(id)delegate {
3635 delegate_ = delegate;
3636 }
3637
3638 - (void) setObject:(id)object {
3639 if (object_ != nil)
3640 [object_ release];
3641 if (object == nil)
3642 object_ = nil;
3643 else
3644 object_ = [object retain];
3645 }
3646
3647 - (void) reloadData {
3648 NSArray *packages = [database_ packages];
3649
3650 [packages_ removeAllObjects];
3651 [sections_ removeAllObjects];
3652
3653 for (size_t i(0); i != [packages count]; ++i) {
3654 Package *package([packages objectAtIndex:i]);
3655 if ([[package performSelector:filter_ withObject:object_] boolValue])
3656 [packages_ addObject:package];
3657 }
3658
3659 Section *section = nil;
3660
3661 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3662 Package *package = [packages_ objectAtIndex:offset];
3663 NSString *name = [package index];
3664
3665 if (section == nil || ![[section name] isEqualToString:name]) {
3666 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3667 [sections_ addObject:section];
3668 }
3669
3670 [section addToCount];
3671 }
3672
3673 [list_ reloadData];
3674 }
3675
3676 - (NSString *) title {
3677 return title_;
3678 }
3679
3680 - (void) resetViewAnimated:(BOOL)animated {
3681 [list_ resetViewAnimated:animated];
3682 }
3683
3684 - (void) resetCursor {
3685 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3686 }
3687
3688 - (UISectionList *) list {
3689 return list_;
3690 }
3691
3692 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3693 [list_ setShouldHideHeaderInShortLists:hide];
3694 }
3695
3696 @end
3697 /* }}} */
3698
3699 /* Add Source View {{{ */
3700 @interface AddSourceView : RVPage {
3701 _transient Database *database_;
3702 }
3703
3704 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3705
3706 @end
3707
3708 @implementation AddSourceView
3709
3710 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3711 if ((self = [super initWithBook:book]) != nil) {
3712 database_ = database;
3713 } return self;
3714 }
3715
3716 @end
3717 /* }}} */
3718 /* Source Cell {{{ */
3719 @interface SourceCell : UITableCell {
3720 UIImage *icon_;
3721 NSString *origin_;
3722 NSString *description_;
3723 NSString *label_;
3724 }
3725
3726 - (void) dealloc;
3727
3728 - (SourceCell *) initWithSource:(Source *)source;
3729
3730 @end
3731
3732 @implementation SourceCell
3733
3734 - (void) dealloc {
3735 [icon_ release];
3736 [origin_ release];
3737 [description_ release];
3738 [label_ release];
3739 [super dealloc];
3740 }
3741
3742 - (SourceCell *) initWithSource:(Source *)source {
3743 if ((self = [super init]) != nil) {
3744 if (icon_ == nil)
3745 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3746 if (icon_ == nil)
3747 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3748 icon_ = [icon_ retain];
3749
3750 origin_ = [[source name] retain];
3751 label_ = [[source uri] retain];
3752 description_ = [[source description] retain];
3753 } return self;
3754 }
3755
3756 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3757 if (icon_ != nil)
3758 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3759
3760 if (selected)
3761 UISetColor(White_);
3762
3763 if (!selected)
3764 UISetColor(Black_);
3765 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3766
3767 if (!selected)
3768 UISetColor(Blue_);
3769 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3770
3771 if (!selected)
3772 UISetColor(Gray_);
3773 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3774
3775 [super drawContentInRect:rect selected:selected];
3776 }
3777
3778 @end
3779 /* }}} */
3780 /* Source Table {{{ */
3781 @interface SourceTable : RVPage {
3782 _transient Database *database_;
3783 UISectionList *list_;
3784 NSMutableArray *sources_;
3785 UIAlertSheet *alert_;
3786 int offset_;
3787
3788 NSString *href_;
3789 UIProgressHUD *hud_;
3790 NSError *error_;
3791
3792 //NSURLConnection *installer_;
3793 NSURLConnection *trivial_bz2_;
3794 NSURLConnection *trivial_gz_;
3795 //NSURLConnection *automatic_;
3796
3797 BOOL trivial_;
3798 }
3799
3800 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3801
3802 @end
3803
3804 @implementation SourceTable
3805
3806 - (void) _deallocConnection:(NSURLConnection *)connection {
3807 if (connection != nil) {
3808 [connection cancel];
3809 //[connection setDelegate:nil];
3810 [connection release];
3811 }
3812 }
3813
3814 - (void) dealloc {
3815 [[list_ table] setDelegate:nil];
3816 [list_ setDataSource:nil];
3817
3818 if (href_ != nil)
3819 [href_ release];
3820 if (hud_ != nil)
3821 [hud_ release];
3822 if (error_ != nil)
3823 [error_ release];
3824
3825 //[self _deallocConnection:installer_];
3826 [self _deallocConnection:trivial_gz_];
3827 [self _deallocConnection:trivial_bz2_];
3828 //[self _deallocConnection:automatic_];
3829
3830 [sources_ release];
3831 [list_ release];
3832 [super dealloc];
3833 }
3834
3835 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3836 return offset_ == 0 ? 1 : 2;
3837 }
3838
3839 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3840 switch (section + (offset_ == 0 ? 1 : 0)) {
3841 case 0: return @"Entered by User";
3842 case 1: return @"Installed by Packages";
3843
3844 default:
3845 _assert(false);
3846 return nil;
3847 }
3848 }
3849
3850 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3851 switch (section + (offset_ == 0 ? 1 : 0)) {
3852 case 0: return 0;
3853 case 1: return offset_;
3854
3855 default:
3856 _assert(false);
3857 return -1;
3858 }
3859 }
3860
3861 - (int) numberOfRowsInTable:(UITable *)table {
3862 return [sources_ count];
3863 }
3864
3865 - (float) table:(UITable *)table heightForRow:(int)row {
3866 Source *source = [sources_ objectAtIndex:row];
3867 return [source description] == nil ? 56 : 73;
3868 }
3869
3870 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3871 Source *source = [sources_ objectAtIndex:row];
3872 // XXX: weird warning, stupid selectors ;P
3873 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3874 }
3875
3876 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3877 return YES;
3878 }
3879
3880 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3881 return YES;
3882 }
3883
3884 - (void) tableRowSelected:(NSNotification*)notification {
3885 UITable *table([list_ table]);
3886 int row([table selectedRow]);
3887 if (row == INT_MAX)
3888 return;
3889
3890 Source *source = [sources_ objectAtIndex:row];
3891
3892 PackageTable *packages = [[[PackageTable alloc]
3893 initWithBook:book_
3894 database:database_
3895 title:[source label]
3896 filter:@selector(isVisibleInSource:)
3897 with:source
3898 ] autorelease];
3899
3900 [packages setDelegate:delegate_];
3901
3902 [book_ pushPage:packages];
3903 }
3904
3905 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3906 Source *source = [sources_ objectAtIndex:row];
3907 return [source record] != nil;
3908 }
3909
3910 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3911 [[list_ table] setDeleteConfirmationRow:row];
3912 }
3913
3914 - (void) table:(UITable *)table deleteRow:(int)row {
3915 Source *source = [sources_ objectAtIndex:row];
3916 [Sources_ removeObjectForKey:[source key]];
3917 [delegate_ syncData];
3918 }
3919
3920 - (void) _endConnection:(NSURLConnection *)connection {
3921 NSURLConnection **field = NULL;
3922 if (connection == trivial_bz2_)
3923 field = &trivial_bz2_;
3924 else if (connection == trivial_gz_)
3925 field = &trivial_gz_;
3926 _assert(field != NULL);
3927 [connection release];
3928 *field = nil;
3929
3930 if (
3931 trivial_bz2_ == nil &&
3932 trivial_gz_ == nil
3933 ) {
3934 [delegate_ setStatusBarShowsProgress:NO];
3935
3936 [hud_ show:NO];
3937 [hud_ removeFromSuperview];
3938 [hud_ autorelease];
3939 hud_ = nil;
3940
3941 if (trivial_) {
3942 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3943 @"deb", @"Type",
3944 href_, @"URI",
3945 @"./", @"Distribution",
3946 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3947
3948 [delegate_ syncData];
3949 } else if (error_ != nil) {
3950 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3951 initWithTitle:@"Verification Error"
3952 buttons:[NSArray arrayWithObjects:@"OK", nil]
3953 defaultButtonIndex:0
3954 delegate:self
3955 context:@"urlerror"
3956 ] autorelease];
3957
3958 [sheet setBodyText:[error_ localizedDescription]];
3959 [sheet popupAlertAnimated:YES];
3960 } else {
3961 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3962 initWithTitle:@"Did not Find Repository"
3963 buttons:[NSArray arrayWithObjects:@"OK", nil]
3964 defaultButtonIndex:0
3965 delegate:self
3966 context:@"trivial"
3967 ] autorelease];
3968
3969 [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."];
3970 [sheet popupAlertAnimated:YES];
3971 }
3972
3973 [href_ release];
3974 href_ = nil;
3975
3976 if (error_ != nil) {
3977 [error_ release];
3978 error_ = nil;
3979 }
3980 }
3981 }
3982
3983 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3984 switch ([response statusCode]) {
3985 case 200:
3986 trivial_ = YES;
3987 }
3988 }
3989
3990 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3991 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3992 if (error_ != nil)
3993 error_ = [error retain];
3994 [self _endConnection:connection];
3995 }
3996
3997 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3998 [self _endConnection:connection];
3999 }
4000
4001 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4002 NSMutableURLRequest *request = [NSMutableURLRequest
4003 requestWithURL:[NSURL URLWithString:href]
4004 cachePolicy:NSURLRequestUseProtocolCachePolicy
4005 timeoutInterval:20.0
4006 ];
4007
4008 [request setHTTPMethod:method];
4009
4010 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4011 }
4012
4013 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4014 NSString *context = [sheet context];
4015 if ([context isEqualToString:@"source"])
4016 switch (button) {
4017 case 1: {
4018 NSString *href = [[sheet textField] text];
4019
4020 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4021
4022 if (![href hasSuffix:@"/"])
4023 href_ = [href stringByAppendingString:@"/"];
4024 else
4025 href_ = href;
4026 href_ = [href_ retain];
4027
4028 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4029 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4030 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4031
4032 trivial_ = false;
4033
4034 hud_ = [delegate_ addProgressHUD];
4035 [hud_ setText:@"Verifying URL"];
4036 } break;
4037
4038 case 2:
4039 break;
4040
4041 default:
4042 _assert(false);
4043 }
4044
4045 [sheet dismiss];
4046 }
4047
4048 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4049 if ((self = [super initWithBook:book]) != nil) {
4050 database_ = database;
4051 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4052
4053 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4054 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4055 [list_ setShouldHideHeaderInShortLists:NO];
4056
4057 [self addSubview:list_];
4058 [list_ setDataSource:self];
4059
4060 UITableColumn *column = [[UITableColumn alloc]
4061 initWithTitle:@"Name"
4062 identifier:@"name"
4063 width:[self frame].size.width
4064 ];
4065
4066 UITable *table = [list_ table];
4067 [table setSeparatorStyle:1];
4068 [table addTableColumn:column];
4069 [table setDelegate:self];
4070
4071 [self reloadData];
4072 } return self;
4073 }
4074
4075 - (void) reloadData {
4076 pkgSourceList list;
4077 _assert(list.ReadMainList());
4078
4079 [sources_ removeAllObjects];
4080 [sources_ addObjectsFromArray:[database_ sources]];
4081 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4082
4083 int count = [sources_ count];
4084 for (offset_ = 0; offset_ != count; ++offset_) {
4085 Source *source = [sources_ objectAtIndex:offset_];
4086 if ([source record] == nil)
4087 break;
4088 }
4089
4090 [list_ reloadData];
4091 }
4092
4093 - (void) resetViewAnimated:(BOOL)animated {
4094 [list_ resetViewAnimated:animated];
4095 }
4096
4097 - (void) _leftButtonClicked {
4098 /*[book_ pushPage:[[[AddSourceView alloc]
4099 initWithBook:book_
4100 database:database_
4101 ] autorelease]];*/
4102
4103 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4104 initWithTitle:@"Enter Cydia/APT URL"
4105 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4106 defaultButtonIndex:0
4107 delegate:self
4108 context:@"source"
4109 ] autorelease];
4110
4111 [sheet addTextFieldWithValue:@"http://" label:@""];
4112
4113 UITextTraits *traits = [[sheet textField] textTraits];
4114 [traits setAutoCapsType:0];
4115 [traits setPreferredKeyboardType:3];
4116 [traits setAutoCorrectionType:1];
4117
4118 [sheet popupAlertAnimated:YES];
4119 }
4120
4121 - (void) _rightButtonClicked {
4122 UITable *table = [list_ table];
4123 BOOL editing = [table isRowDeletionEnabled];
4124 [table enableRowDeletion:!editing animated:YES];
4125 [book_ reloadButtonsForPage:self];
4126 }
4127
4128 - (NSString *) title {
4129 return @"Sources";
4130 }
4131
4132 - (NSString *) backButtonTitle {
4133 return @"Sources";
4134 }
4135
4136 - (NSString *) leftButtonTitle {
4137 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4138 }
4139
4140 - (NSString *) rightButtonTitle {
4141 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4142 }
4143
4144 - (RVUINavBarButtonStyle) rightButtonStyle {
4145 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4146 }
4147
4148 @end
4149 /* }}} */
4150
4151 /* Installed View {{{ */
4152 @interface InstalledView : RVPage {
4153 _transient Database *database_;
4154 PackageTable *packages_;
4155 BOOL expert_;
4156 }
4157
4158 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4159
4160 @end
4161
4162 @implementation InstalledView
4163
4164 - (void) dealloc {
4165 [packages_ release];
4166 [super dealloc];
4167 }
4168
4169 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4170 if ((self = [super initWithBook:book]) != nil) {
4171 database_ = database;
4172
4173 packages_ = [[PackageTable alloc]
4174 initWithBook:book
4175 database:database
4176 title:nil
4177 filter:@selector(isInstalledAndVisible:)
4178 with:[NSNumber numberWithBool:YES]
4179 ];
4180
4181 [self addSubview:packages_];
4182 } return self;
4183 }
4184
4185 - (void) resetViewAnimated:(BOOL)animated {
4186 [packages_ resetViewAnimated:animated];
4187 }
4188
4189 - (void) reloadData {
4190 [packages_ reloadData];
4191 }
4192
4193 - (void) _rightButtonClicked {
4194 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4195 [packages_ reloadData];
4196 expert_ = !expert_;
4197 [book_ reloadButtonsForPage:self];
4198 }
4199
4200 - (NSString *) title {
4201 return @"Installed";
4202 }
4203
4204 - (NSString *) backButtonTitle {
4205 return @"Packages";
4206 }
4207
4208 - (NSString *) rightButtonTitle {
4209 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4210 }
4211
4212 - (RVUINavBarButtonStyle) rightButtonStyle {
4213 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4214 }
4215
4216 - (void) setDelegate:(id)delegate {
4217 [super setDelegate:delegate];
4218 [packages_ setDelegate:delegate];
4219 }
4220
4221 @end
4222 /* }}} */
4223
4224 /* Home View {{{ */
4225 @interface HomeView : BrowserView {
4226 }
4227
4228 @end
4229
4230 @implementation HomeView
4231
4232 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4233 [sheet dismiss];
4234 }
4235
4236 - (void) _leftButtonClicked {
4237 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4238 initWithTitle:@"About Cydia Installer"
4239 buttons:[NSArray arrayWithObjects:@"Close", nil]
4240 defaultButtonIndex:0
4241 delegate:self
4242 context:@"about"
4243 ] autorelease];
4244
4245 [sheet setBodyText:
4246 @"Copyright (C) 2008\n"
4247 "Jay Freeman (saurik)\n"
4248 "saurik@saurik.com\n"
4249 "http://www.saurik.com/\n"
4250 "\n"
4251 "The Okori Group\n"
4252 "http://www.theokorigroup.com/\n"
4253 "\n"
4254 "College of Creative Studies,\n"
4255 "University of California,\n"
4256 "Santa Barbara\n"
4257 "http://www.ccs.ucsb.edu/"
4258 ];
4259
4260 [sheet popupAlertAnimated:YES];
4261 }
4262
4263 - (NSString *) leftButtonTitle {
4264 return @"About";
4265 }
4266
4267 @end
4268 /* }}} */
4269 /* Manage View {{{ */
4270 @interface ManageView : BrowserView {
4271 }
4272
4273 @end
4274
4275 @implementation ManageView
4276
4277 - (NSString *) title {
4278 return @"Manage";
4279 }
4280
4281 - (void) _leftButtonClicked {
4282 [delegate_ askForSettings];
4283 }
4284
4285 - (NSString *) leftButtonTitle {
4286 return @"Settings";
4287 }
4288
4289 - (NSString *) rightButtonTitle {
4290 return nil;
4291 }
4292
4293 @end
4294 /* }}} */
4295
4296 /* Browser Implementation {{{ */
4297 @implementation BrowserView
4298
4299 - (void) dealloc {
4300 WebView *webview = [webview_ webView];
4301 [webview setFrameLoadDelegate:nil];
4302 [webview setResourceLoadDelegate:nil];
4303 [webview setUIDelegate:nil];
4304
4305 [scroller_ setDelegate:nil];
4306 [webview_ setDelegate:nil];
4307
4308 [scroller_ release];
4309 [webview_ release];
4310 [urls_ release];
4311 [indicator_ release];
4312 if (title_ != nil)
4313 [title_ release];
4314 [super dealloc];
4315 }
4316
4317 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4318 [self loadRequest:[NSURLRequest
4319 requestWithURL:url
4320 cachePolicy:policy
4321 timeoutInterval:30.0
4322 ]];
4323 }
4324
4325 - (void) loadURL:(NSURL *)url {
4326 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4327 }
4328
4329 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4330 NSMutableURLRequest *copy = [request mutableCopy];
4331
4332 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4333 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4334 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4335
4336 if (Role_ != nil)
4337 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4338
4339 return copy;
4340 }
4341
4342 - (void) loadRequest:(NSURLRequest *)request {
4343 pushed_ = true;
4344 [webview_ loadRequest:request];
4345 }
4346
4347 - (void) reloadURL {
4348 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4349 [urls_ removeLastObject];
4350 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4351 }
4352
4353 - (WebView *) webView {
4354 return [webview_ webView];
4355 }
4356
4357 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4358 [scroller_ setContentSize:frame.size];
4359 }
4360
4361 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4362 [self view:sender didSetFrame:frame];
4363 }
4364
4365 - (void) pushPage:(RVPage *)page {
4366 [self setBackButtonTitle:title_];
4367 [page setDelegate:delegate_];
4368 [book_ pushPage:page];
4369 }
4370
4371 - (void) getSpecial:(NSString *)href {
4372 RVPage *page = nil;
4373
4374 if ([href hasPrefix:@"mailto:"])
4375 [delegate_ openURL:[NSURL URLWithString:href]];
4376 else if ([href isEqualToString:@"cydia://add-source"])
4377 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4378 else if ([href isEqualToString:@"cydia://sources"])
4379 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4380 else if ([href isEqualToString:@"cydia://packages"])
4381 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4382 else if ([href hasPrefix:@"apptapp://package/"]) {
4383 NSString *name = [href substringFromIndex:18];
4384
4385 if (Package *package = [database_ packageWithName:name]) {
4386 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4387 [view setPackage:package];
4388 page = view;
4389 } else {
4390 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4391 initWithTitle:@"Cannot Locate Package"
4392 buttons:[NSArray arrayWithObjects:@"Close", nil]
4393 defaultButtonIndex:0
4394 delegate:self
4395 context:@"missing"
4396 ] autorelease];
4397
4398 [sheet setBodyText:[NSString stringWithFormat:
4399 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4400 , name]];
4401
4402 [sheet popupAlertAnimated:YES];
4403 }
4404 }
4405
4406 if (page != nil)
4407 [self pushPage:page];
4408 }
4409
4410 - (void) webView:(WebView *)sender willClickElement:(id)element {
4411 if ([[element localName] isEqualToString:@"img"])
4412 do if ((element = [element parentNode]) == nil)
4413 return;
4414 while (![[element localName] isEqualToString:@"a"]);
4415 if (![element respondsToSelector:@selector(href)])
4416 return;
4417 NSString *href = [element href];
4418 if (href == nil)
4419 return;
4420 [self getSpecial:href];
4421 }
4422
4423 - (BOOL) isSpecialScheme:(NSString *)scheme {
4424 return
4425 [scheme isEqualToString:@"apptapp"] ||
4426 [scheme isEqualToString:@"cydia"] ||
4427 [scheme isEqualToString:@"mailto"];
4428 }
4429
4430 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4431 NSURL *url = [request URL];
4432 if ([self isSpecialScheme:[url scheme]]) {
4433 [self getSpecial:[url absoluteString]];
4434 return nil;
4435 }
4436
4437 if (!pushed_) {
4438 pushed_ = true;
4439 [book_ pushPage:self];
4440 }
4441
4442 return [self _addHeadersToRequest:request];
4443 }
4444
4445 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4446 if (request != nil) {
4447 NSString *scheme = [[request URL] scheme];
4448 if ([self isSpecialScheme:scheme])
4449 return nil;
4450 }
4451
4452 [self setBackButtonTitle:title_];
4453
4454 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4455 [browser setDelegate:delegate_];
4456
4457 if (request != nil) {
4458 [browser loadRequest:[self _addHeadersToRequest:request]];
4459 [book_ pushPage:browser];
4460 }
4461
4462 return [browser webView];
4463 }
4464
4465 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4466 title_ = [title retain];
4467 [self setTitle:title];
4468 }
4469
4470 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4471 if ([frame parentFrame] != nil)
4472 return;
4473
4474 reloading_ = false;
4475 loading_ = true;
4476 [indicator_ startAnimation];
4477 [self reloadButtons];
4478
4479 if (title_ != nil) {
4480 [title_ release];
4481 title_ = nil;
4482 }
4483
4484 [self setTitle:@"Loading..."];
4485
4486 WebView *webview = [webview_ webView];
4487 NSString *href = [webview mainFrameURL];
4488 [urls_ addObject:[NSURL URLWithString:href]];
4489
4490 CGRect webrect = [scroller_ frame];
4491 webrect.size.height = 0;
4492 [webview_ setFrame:webrect];
4493 }
4494
4495 - (void) _finishLoading {
4496 if (!reloading_) {
4497 loading_ = false;
4498 [indicator_ stopAnimation];
4499 [self reloadButtons];
4500 }
4501 }
4502
4503 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4504 if ([frame parentFrame] != nil)
4505 return;
4506 [self _finishLoading];
4507 }
4508
4509 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4510 if ([frame parentFrame] != nil)
4511 return;
4512 [self _finishLoading];
4513
4514 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4515 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4516 [[error localizedDescription] stringByAddingPercentEscapes]
4517 ]]];
4518 }
4519
4520 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4521 if ((self = [super initWithBook:book]) != nil) {
4522 database_ = database;
4523 loading_ = false;
4524
4525 struct CGRect bounds = [self bounds];
4526
4527 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4528 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4529 [self addSubview:pinstripe];
4530
4531 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4532 [self addSubview:scroller_];
4533
4534 [scroller_ setScrollingEnabled:YES];
4535 [scroller_ setAdjustForContentSizeChange:YES];
4536 [scroller_ setClipsSubviews:YES];
4537 [scroller_ setAllowsRubberBanding:YES];
4538 [scroller_ setScrollDecelerationFactor:0.99];
4539 [scroller_ setDelegate:self];
4540
4541 CGRect webrect = [scroller_ bounds];
4542 webrect.size.height = 0;
4543
4544 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4545 [scroller_ addSubview:webview_];
4546
4547 [webview_ setTilingEnabled:YES];
4548 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4549 [webview_ setAutoresizes:YES];
4550 [webview_ setDelegate:self];
4551 //[webview_ setEnabledGestures:2];
4552
4553 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4554 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4555 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4556
4557 Package *package([database_ packageWithName:@"cydia"]);
4558 NSString *application = package == nil ? @"Cydia" : [NSString
4559 stringWithFormat:@"Cydia/%@",
4560 [package installed]
4561 ];
4562
4563 WebView *webview = [webview_ webView];
4564 [webview setApplicationNameForUserAgent:application];
4565 [webview setFrameLoadDelegate:self];
4566 [webview setResourceLoadDelegate:self];
4567 [webview setUIDelegate:self];
4568
4569 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4570 } return self;
4571 }
4572
4573 - (void) _rightButtonClicked {
4574 reloading_ = true;
4575 [self reloadURL];
4576 }
4577
4578 - (NSString *) rightButtonTitle {
4579 return loading_ ? @"" : @"Reload";
4580 }
4581
4582 - (NSString *) title {
4583 return nil;
4584 }
4585
4586 - (NSString *) backButtonTitle {
4587 return @"Browser";
4588 }
4589
4590 - (void) setPageActive:(BOOL)active {
4591 if (active)
4592 [book_ addSubview:indicator_];
4593 else
4594 [indicator_ removeFromSuperview];
4595 }
4596
4597 - (void) resetViewAnimated:(BOOL)animated {
4598 }
4599
4600 - (void) setPushed:(bool)pushed {
4601 pushed_ = pushed;
4602 }
4603
4604 @end
4605 /* }}} */
4606
4607 @interface CYBook : RVBook <
4608 ProgressDelegate
4609 > {
4610 _transient Database *database_;
4611 UIView *overlay_;
4612 UIProgressIndicator *indicator_;
4613 UITextLabel *prompt_;
4614 UIProgressBar *progress_;
4615 bool updating_;
4616 }
4617
4618 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4619 - (void) update;
4620 - (BOOL) updating;
4621
4622 @end
4623
4624 /* Install View {{{ */
4625 @interface InstallView : RVPage {
4626 _transient Database *database_;
4627 NSMutableArray *sections_;
4628 NSMutableArray *filtered_;
4629 UITransitionView *transition_;
4630 UITable *list_;
4631 UIView *accessory_;
4632 BOOL editing_;
4633 }
4634
4635 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4636 - (void) reloadData;
4637 - (void) resetView;
4638
4639 @end
4640
4641 @implementation InstallView
4642
4643 - (void) dealloc {
4644 [list_ setDataSource:nil];
4645 [list_ setDelegate:nil];
4646
4647 [sections_ release];
4648 [filtered_ release];
4649 [transition_ release];
4650 [list_ release];
4651 [accessory_ release];
4652 [super dealloc];
4653 }
4654
4655 - (int) numberOfRowsInTable:(UITable *)table {
4656 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4657 }
4658
4659 - (float) table:(UITable *)table heightForRow:(int)row {
4660 return 45;
4661 }
4662
4663 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4664 if (reusing == nil)
4665 reusing = [[[SectionCell alloc] init] autorelease];
4666 [(SectionCell *)reusing setSection:(editing_ ?
4667 [sections_ objectAtIndex:row] :
4668 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4669 ) editing:editing_];
4670 return reusing;
4671 }
4672
4673 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4674 return !editing_;
4675 }
4676
4677 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4678 return !editing_;
4679 }
4680
4681 - (void) tableRowSelected:(NSNotification *)notification {
4682 int row = [[notification object] selectedRow];
4683 if (row == INT_MAX)
4684 return;
4685
4686 Section *section;
4687 NSString *name;
4688 NSString *title;
4689
4690 if (row == 0) {
4691 section = nil;
4692 name = nil;
4693 title = @"All Packages";
4694 } else {
4695 section = [filtered_ objectAtIndex:(row - 1)];
4696 name = [section name];
4697
4698 if (name != nil)
4699 title = name;
4700 else {
4701 name = @"";
4702 title = @"(No Section)";
4703 }
4704 }
4705
4706 PackageTable *table = [[[PackageTable alloc]
4707 initWithBook:book_
4708 database:database_
4709 title:title
4710 filter:@selector(isVisiblyUninstalledInSection:)
4711 with:name
4712 ] autorelease];
4713
4714 [table setDelegate:delegate_];
4715
4716 [book_ pushPage:table];
4717 }
4718
4719 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4720 if ((self = [super initWithBook:book]) != nil) {
4721 database_ = database;
4722
4723 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4724 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4725
4726 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4727 [self addSubview:transition_];
4728
4729 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4730 [transition_ transition:0 toView:list_];
4731
4732 UITableColumn *column = [[[UITableColumn alloc]
4733 initWithTitle:@"Name"
4734 identifier:@"name"
4735 width:[self frame].size.width
4736 ] autorelease];
4737
4738 [list_ setDataSource:self];
4739 [list_ setSeparatorStyle:1];
4740 [list_ addTableColumn:column];
4741 [list_ setDelegate:self];
4742 [list_ setReusesTableCells:YES];
4743
4744 [self reloadData];
4745 } return self;
4746 }
4747
4748 - (void) reloadData {
4749 NSArray *packages = [database_ packages];
4750
4751 [sections_ removeAllObjects];
4752 [filtered_ removeAllObjects];
4753
4754 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4755 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4756
4757 for (size_t i(0); i != [packages count]; ++i) {
4758 Package *package([packages objectAtIndex:i]);
4759 NSString *name([package section]);
4760
4761 if (name != nil) {
4762 Section *section([sections objectForKey:name]);
4763 if (section == nil) {
4764 section = [[[Section alloc] initWithName:name] autorelease];
4765 [sections setObject:section forKey:name];
4766 }
4767 }
4768
4769 if ([package valid] && [package installed] == nil && [package visible])
4770 [filtered addObject:package];
4771 }
4772
4773 [sections_ addObjectsFromArray:[sections allValues]];
4774 [sections_ sortUsingSelector:@selector(compareByName:)];
4775
4776 [filtered sortUsingSelector:@selector(compareBySection:)];
4777
4778 Section *section = nil;
4779 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4780 Package *package = [filtered objectAtIndex:offset];
4781 NSString *name = [package section];
4782
4783 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4784 section = name == nil ?
4785 [[[Section alloc] initWithName:nil] autorelease] :
4786 [sections objectForKey:name];
4787 [filtered_ addObject:section];
4788 }
4789
4790 [section addToCount];
4791 }
4792
4793 [list_ reloadData];
4794 }
4795
4796 - (void) resetView {
4797 if (editing_)
4798 [self _rightButtonClicked];
4799 }
4800
4801 - (void) resetViewAnimated:(BOOL)animated {
4802 [list_ resetViewAnimated:animated];
4803 }
4804
4805 - (void) _rightButtonClicked {
4806 if ((editing_ = !editing_))
4807 [list_ reloadData];
4808 else {
4809 [delegate_ updateData];
4810 }
4811
4812 [book_ setTitle:[self title] forPage:self];
4813 [book_ reloadButtonsForPage:self];
4814 }
4815
4816 - (NSString *) title {
4817 return editing_ ? @"Section Visibility" : @"Install by Section";
4818 }
4819
4820 - (NSString *) backButtonTitle {
4821 return @"Sections";
4822 }
4823
4824 - (NSString *) rightButtonTitle {
4825 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4826 }
4827
4828 - (UIView *) accessoryView {
4829 return accessory_;
4830 }
4831
4832 @end
4833 /* }}} */
4834 /* Changes View {{{ */
4835 @interface ChangesView : RVPage {
4836 _transient Database *database_;
4837 NSMutableArray *packages_;
4838 NSMutableArray *sections_;
4839 UISectionList *list_;
4840 unsigned upgrades_;
4841 }
4842
4843 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4844 - (void) reloadData;
4845
4846 @end
4847
4848 @implementation ChangesView
4849
4850 - (void) dealloc {
4851 [[list_ table] setDelegate:nil];
4852 [list_ setDataSource:nil];
4853
4854 [packages_ release];
4855 [sections_ release];
4856 [list_ release];
4857 [super dealloc];
4858 }
4859
4860 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4861 return [sections_ count];
4862 }
4863
4864 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4865 return [[sections_ objectAtIndex:section] name];
4866 }
4867
4868 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4869 return [[sections_ objectAtIndex:section] row];
4870 }
4871
4872 - (int) numberOfRowsInTable:(UITable *)table {
4873 return [packages_ count];
4874 }
4875
4876 - (float) table:(UITable *)table heightForRow:(int)row {
4877 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4878 }
4879
4880 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4881 if (reusing == nil)
4882 reusing = [[[PackageCell alloc] init] autorelease];
4883 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4884 return reusing;
4885 }
4886
4887 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4888 return NO;
4889 }
4890
4891 - (void) tableRowSelected:(NSNotification *)notification {
4892 int row = [[notification object] selectedRow];
4893 if (row == INT_MAX)
4894 return;
4895 Package *package = [packages_ objectAtIndex:row];
4896 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4897 [view setDelegate:delegate_];
4898 [view setPackage:package];
4899 [book_ pushPage:view];
4900 }
4901
4902 - (void) _leftButtonClicked {
4903 [(CYBook *)book_ update];
4904 [self reloadButtons];
4905 }
4906
4907 - (void) _rightButtonClicked {
4908 [delegate_ distUpgrade];
4909 }
4910
4911 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4912 if ((self = [super initWithBook:book]) != nil) {
4913 database_ = database;
4914
4915 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4916 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4917
4918 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4919 [self addSubview:list_];
4920
4921 [list_ setShouldHideHeaderInShortLists:NO];
4922 [list_ setDataSource:self];
4923 //[list_ setSectionListStyle:1];
4924
4925 UITableColumn *column = [[[UITableColumn alloc]
4926 initWithTitle:@"Name"
4927 identifier:@"name"
4928 width:[self frame].size.width
4929 ] autorelease];
4930
4931 UITable *table = [list_ table];
4932 [table setSeparatorStyle:1];
4933 [table addTableColumn:column];
4934 [table setDelegate:self];
4935 [table setReusesTableCells:YES];
4936
4937 [self reloadData];
4938 } return self;
4939 }
4940
4941 - (void) reloadData {
4942 NSArray *packages = [database_ packages];
4943
4944 [packages_ removeAllObjects];
4945 [sections_ removeAllObjects];
4946
4947 for (size_t i(0); i != [packages count]; ++i) {
4948 Package *package([packages objectAtIndex:i]);
4949
4950 if (
4951 [package installed] == nil && [package valid] && [package visible] ||
4952 [package upgradableAndEssential:NO]
4953 )
4954 [packages_ addObject:package];
4955 }
4956
4957 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4958
4959 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4960 Section *section = nil;
4961
4962 upgrades_ = 0;
4963 bool unseens = false;
4964
4965 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4966
4967 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4968 Package *package = [packages_ objectAtIndex:offset];
4969
4970 if ([package upgradableAndEssential:YES]) {
4971 ++upgrades_;
4972 [upgradable addToCount];
4973 } else {
4974 unseens = true;
4975 NSDate *seen = [package seen];
4976
4977 NSString *name;
4978
4979 if (seen == nil)
4980 name = [@"n/a ?" retain];
4981 else {
4982 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4983 }
4984
4985 if (section == nil || ![[section name] isEqualToString:name]) {
4986 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4987 [sections_ addObject:section];
4988 }
4989
4990 [name release];
4991 [section addToCount];
4992 }
4993 }
4994
4995 CFRelease(formatter);
4996
4997 if (unseens) {
4998 Section *last = [sections_ lastObject];
4999 size_t count = [last count];
5000 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5001 [sections_ removeLastObject];
5002 }
5003
5004 if (upgrades_ != 0)
5005 [sections_ insertObject:upgradable atIndex:0];
5006
5007 [list_ reloadData];
5008 [self reloadButtons];
5009 }
5010
5011 - (void) resetViewAnimated:(BOOL)animated {
5012 [list_ resetViewAnimated:animated];
5013 }
5014
5015 - (NSString *) leftButtonTitle {
5016 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5017 }
5018
5019 - (NSString *) rightButtonTitle {
5020 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5021 }
5022
5023 - (NSString *) title {
5024 return @"Changes";
5025 }
5026
5027 @end
5028 /* }}} */
5029 /* Search View {{{ */
5030 @protocol SearchViewDelegate
5031 - (void) showKeyboard:(BOOL)show;
5032 @end
5033
5034 @interface SearchView : RVPage {
5035 UIView *accessory_;
5036 UISearchField *field_;
5037 UITransitionView *transition_;
5038 PackageTable *table_;
5039 UIPreferencesTable *advanced_;
5040 UIView *dimmed_;
5041 bool flipped_;
5042 bool reload_;
5043 }
5044
5045 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5046 - (void) reloadData;
5047
5048 @end
5049
5050 @implementation SearchView
5051
5052 - (void) dealloc {
5053 #ifndef __OBJC2__
5054 [[field_ textTraits] setEditingDelegate:nil];
5055 #endif
5056 [field_ setDelegate:nil];
5057
5058 [accessory_ release];
5059 [field_ release];
5060 [transition_ release];
5061 [table_ release];
5062 [advanced_ release];
5063 [dimmed_ release];
5064 [super dealloc];
5065 }
5066
5067 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5068 return 1;
5069 }
5070
5071 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5072 switch (group) {
5073 case 0: return @"Advanced Search (Coming Soon!)";
5074
5075 default: _assert(false);
5076 }
5077 }
5078
5079 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5080 switch (group) {
5081 case 0: return 0;
5082
5083 default: _assert(false);
5084 }
5085 }
5086
5087 - (void) _showKeyboard:(BOOL)show {
5088 CGSize keysize = [UIKeyboard defaultSize];
5089 CGRect keydown = [book_ pageBounds];
5090 CGRect keyup = keydown;
5091 keyup.size.height -= keysize.height - ButtonBarHeight_;
5092
5093 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5094
5095 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5096 [animation setSignificantRectFields:8];
5097
5098 if (show) {
5099 [animation setStartFrame:keydown];
5100 [animation setEndFrame:keyup];
5101 } else {
5102 [animation setStartFrame:keyup];
5103 [animation setEndFrame:keydown];
5104 }
5105
5106 UIAnimator *animator = [UIAnimator sharedAnimator];
5107
5108 [animator
5109 addAnimations:[NSArray arrayWithObjects:animation, nil]
5110 withDuration:(KeyboardTime_ - delay)
5111 start:!show
5112 ];
5113
5114 if (show)
5115 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5116
5117 #ifndef __OBJC2__
5118 [delegate_ showKeyboard:show];
5119 #endif
5120 }
5121
5122 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5123 [self _showKeyboard:YES];
5124 }
5125
5126 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5127 [self _showKeyboard:NO];
5128 }
5129
5130 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5131 if (reload_) {
5132 NSString *text([field_ text]);
5133 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5134 [self reloadData];
5135 reload_ = false;
5136 }
5137 }
5138
5139 - (void) textFieldClearButtonPressed:(UITextField *)field {
5140 reload_ = true;
5141 }
5142
5143 - (void) keyboardInputShouldDelete:(id)input {
5144 reload_ = true;
5145 }
5146
5147 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5148 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5149 reload_ = true;
5150 return YES;
5151 } else {
5152 [field_ resignFirstResponder];
5153 return NO;
5154 }
5155 }
5156
5157 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5158 if ((self = [super initWithBook:book]) != nil) {
5159 CGRect pageBounds = [book_ pageBounds];
5160
5161 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5162 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5163 [self addSubview:pinstripe];*/
5164
5165 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5166 [self addSubview:transition_];
5167
5168 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5169
5170 [advanced_ setReusesTableCells:YES];
5171 [advanced_ setDataSource:self];
5172 [advanced_ reloadData];
5173
5174 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5175 CGColor dimmed(space_, 0, 0, 0, 0.5);
5176 [dimmed_ setBackgroundColor:dimmed];
5177
5178 table_ = [[PackageTable alloc]
5179 initWithBook:book
5180 database:database
5181 title:nil
5182 filter:@selector(isVisiblySearchedForBy:)
5183 with:nil
5184 ];
5185
5186 [table_ setShouldHideHeaderInShortLists:NO];
5187 [transition_ transition:0 toView:table_];
5188
5189 CGRect cnfrect = {{
5190 #ifdef __OBJC2__
5191 6 +
5192 #endif
5193 1, 38}, {17, 18}};
5194
5195 CGRect area;
5196 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5197 area.origin.y = 30;
5198
5199 area.size.width =
5200 #ifdef __OBJC2__
5201 8 +
5202 #endif
5203 [self bounds].size.width - area.origin.x - 18;
5204
5205 area.size.height = [UISearchField defaultHeight];
5206
5207 field_ = [[UISearchField alloc] initWithFrame:area];
5208
5209 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
5210 [field_ setFont:font];
5211 CFRelease(font);
5212
5213 [field_ setPlaceholder:@"Package Names & Descriptions"];
5214 [field_ setDelegate:self];
5215
5216 #ifdef __OBJC2__
5217 [field_ setPaddingTop:3];
5218 #else
5219 [field_ setPaddingTop:5];
5220 #endif
5221
5222 UITextTraits *traits = [field_ textTraits];
5223 [traits setAutoCapsType:0];
5224 [traits setAutoCorrectionType:1];
5225 [traits setReturnKeyType:6];
5226
5227 #ifndef __OBJC2__
5228 [traits setEditingDelegate:self];
5229 #endif
5230
5231 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5232
5233 accessory_ = [[UIView alloc] initWithFrame:accrect];
5234 [accessory_ addSubview:field_];
5235
5236 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5237 [configure setShowPressFeedback:YES];
5238 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5239 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5240 [accessory_ addSubview:configure];*/
5241 } return self;
5242 }
5243
5244 - (void) flipPage {
5245 #ifndef __OBJC2__
5246 LKAnimation *animation = [LKTransition animation];
5247 [animation setType:@"oglFlip"];
5248 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5249 [animation setFillMode:@"extended"];
5250 [animation setTransitionFlags:3];
5251 [animation setDuration:10];
5252 [animation setSpeed:0.35];
5253 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5254 [[transition_ _layer] addAnimation:animation forKey:0];
5255 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5256 flipped_ = !flipped_;
5257 #endif
5258 }
5259
5260 - (void) configurePushed {
5261 [field_ resignFirstResponder];
5262 [self flipPage];
5263 }
5264
5265 - (void) resetViewAnimated:(BOOL)animated {
5266 if (flipped_)
5267 [self flipPage];
5268 [table_ resetViewAnimated:animated];
5269 }
5270
5271 - (void) reloadData {
5272 if (flipped_)
5273 [self flipPage];
5274 [table_ setObject:[field_ text]];
5275 [table_ reloadData];
5276 [table_ resetCursor];
5277 }
5278
5279 - (UIView *) accessoryView {
5280 return accessory_;
5281 }
5282
5283 - (NSString *) title {
5284 return nil;
5285 }
5286
5287 - (NSString *) backButtonTitle {
5288 return @"Search";
5289 }
5290
5291 - (void) setDelegate:(id)delegate {
5292 [table_ setDelegate:delegate];
5293 [super setDelegate:delegate];
5294 }
5295
5296 @end
5297 /* }}} */
5298
5299 @implementation CYBook
5300
5301 - (void) dealloc {
5302 [overlay_ release];
5303 [indicator_ release];
5304 [prompt_ release];
5305 [progress_ release];
5306 [super dealloc];
5307 }
5308
5309 - (NSString *) getTitleForPage:(RVPage *)page {
5310 return Simplify([super getTitleForPage:page]);
5311 }
5312
5313 - (BOOL) updating {
5314 return updating_;
5315 }
5316
5317 - (void) update {
5318 [navbar_ setPrompt:@""];
5319 [navbar_ addSubview:overlay_];
5320 [indicator_ startAnimation];
5321 [prompt_ setText:@"Updating Database..."];
5322 [progress_ setProgress:0];
5323
5324 updating_ = true;
5325
5326 [NSThread
5327 detachNewThreadSelector:@selector(_update)
5328 toTarget:self
5329 withObject:nil
5330 ];
5331 }
5332
5333 - (void) _update_ {
5334 updating_ = false;
5335
5336 [overlay_ removeFromSuperview];
5337 [indicator_ stopAnimation];
5338 [delegate_ reloadData];
5339
5340 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5341 }
5342
5343 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5344 if ((self = [super initWithFrame:frame]) != nil) {
5345 database_ = database;
5346
5347 if (Advanced_)
5348 [navbar_ setBarStyle:1];
5349
5350 CGRect ovrrect = [navbar_ bounds];
5351 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5352
5353 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5354
5355 UIProgressIndicatorStyle style = Advanced_ ?
5356 kUIProgressIndicatorStyleSmallWhite :
5357 kUIProgressIndicatorStyleSmallBlack;
5358
5359 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5360 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5361 CGRect indrect = {{indoffset, indoffset}, indsize};
5362
5363 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5364 [indicator_ setStyle:style];
5365 [overlay_ addSubview:indicator_];
5366
5367 CGSize prmsize = {200, indsize.width + 4};
5368
5369 CGRect prmrect = {{
5370 indoffset * 2 + indsize.width,
5371 #ifdef __OBJC2__
5372 -1 +
5373 #endif
5374 (ovrrect.size.height - prmsize.height) / 2
5375 }, prmsize};
5376
5377 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
5378
5379 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5380
5381 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
5382 [prompt_ setBackgroundColor:Clear_];
5383 [prompt_ setFont:font];
5384
5385 CFRelease(font);
5386
5387 [overlay_ addSubview:prompt_];
5388
5389 CGSize prgsize = {75, 100};
5390
5391 CGRect prgrect = {{
5392 ovrrect.size.width - prgsize.width - 10,
5393 (ovrrect.size.height - prgsize.height) / 2
5394 } , prgsize};
5395
5396 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5397 [progress_ setStyle:0];
5398 [overlay_ addSubview:progress_];
5399 } return self;
5400 }
5401
5402 - (void) _update {
5403 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5404
5405 Status status;
5406 status.setDelegate(self);
5407
5408 [database_ updateWithStatus:status];
5409
5410 [self
5411 performSelectorOnMainThread:@selector(_update_)
5412 withObject:nil
5413 waitUntilDone:NO
5414 ];
5415
5416 [pool release];
5417 }
5418
5419 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5420 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5421 }
5422
5423 - (void) setProgressTitle:(NSString *)title {
5424 [self
5425 performSelectorOnMainThread:@selector(_setProgressTitle:)
5426 withObject:title
5427 waitUntilDone:YES
5428 ];
5429 }
5430
5431 - (void) setProgressPercent:(float)percent {
5432 }
5433
5434 - (void) addProgressOutput:(NSString *)output {
5435 [self
5436 performSelectorOnMainThread:@selector(_addProgressOutput:)
5437 withObject:output
5438 waitUntilDone:YES
5439 ];
5440 }
5441
5442 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5443 [sheet dismiss];
5444 }
5445
5446 - (void) _setProgressTitle:(NSString *)title {
5447 [prompt_ setText:[title stringByAppendingString:@"..."]];
5448 }
5449
5450 - (void) _addProgressOutput:(NSString *)output {
5451 }
5452
5453 @end
5454
5455 @interface Cydia : UIApplication <
5456 ConfirmationViewDelegate,
5457 ProgressViewDelegate,
5458 SearchViewDelegate,
5459 CydiaDelegate
5460 > {
5461 UIWindow *window_;
5462
5463 UIView *underlay_;
5464 UIView *overlay_;
5465 CYBook *book_;
5466 UIButtonBar *buttonbar_;
5467
5468 ConfirmationView *confirm_;
5469
5470 NSMutableArray *essential_;
5471 NSMutableArray *broken_;
5472
5473 Database *database_;
5474 ProgressView *progress_;
5475
5476 unsigned tag_;
5477
5478 UIKeyboard *keyboard_;
5479 UIProgressHUD *hud_;
5480
5481 InstallView *install_;
5482 ChangesView *changes_;
5483 ManageView *manage_;
5484 SearchView *search_;
5485 }
5486
5487 @end
5488
5489 @implementation Cydia
5490
5491 - (void) _loaded {
5492 if ([broken_ count] != 0) {
5493 int count = [broken_ count];
5494
5495 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5496 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5497 buttons:[NSArray arrayWithObjects:
5498 @"Forcibly Clear",
5499 @"Ignore (Temporary)",
5500 nil]
5501 defaultButtonIndex:0
5502 delegate:self
5503 context:@"fixhalf"
5504 ] autorelease];
5505
5506 [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."];
5507 [sheet popupAlertAnimated:YES];
5508 } else if (!Ignored_ && [essential_ count] != 0) {
5509 int count = [essential_ count];
5510
5511 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5512 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5513 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5514 defaultButtonIndex:0
5515 delegate:self
5516 context:@"upgrade"
5517 ] autorelease];
5518
5519 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5520 [sheet popupAlertAnimated:YES];
5521 }
5522 }
5523
5524 - (void) _reloadData {
5525 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5526 [hud setText:@"Reloading Data"];
5527 [overlay_ addSubview:hud];
5528 [hud show:YES];*/
5529
5530 [database_ reloadData];
5531
5532 size_t changes(0);
5533
5534 [essential_ removeAllObjects];
5535 [broken_ removeAllObjects];
5536
5537 NSArray *packages = [database_ packages];
5538 for (int i(0), e([packages count]); i != e; ++i) {
5539 Package *package = [packages objectAtIndex:i];
5540 if ([package half])
5541 [broken_ addObject:package];
5542 if ([package upgradableAndEssential:NO]) {
5543 if ([package essential])
5544 [essential_ addObject:package];
5545 ++changes;
5546 }
5547 }
5548
5549 if (changes != 0) {
5550 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5551 [buttonbar_ setBadgeValue:badge forButton:3];
5552 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5553 [buttonbar_ setBadgeAnimated:YES forButton:3];
5554 [self setApplicationBadge:badge];
5555 } else {
5556 [buttonbar_ setBadgeValue:nil forButton:3];
5557 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5558 [buttonbar_ setBadgeAnimated:NO forButton:3];
5559 [self removeApplicationBadge];
5560 }
5561
5562 [self updateData];
5563
5564 /*if ([packages count] == 0);
5565 else if (Loaded_)*/
5566 [self _loaded];
5567 /*else {
5568 Loaded_ = YES;
5569 [book_ update];
5570 }*/
5571
5572 /*[hud show:NO];
5573 [hud removeFromSuperview];*/
5574 }
5575
5576 - (void) _saveConfig {
5577 if (Changed_) {
5578 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5579 Changed_ = false;
5580 }
5581 }
5582
5583 - (void) updateData {
5584 [self _saveConfig];
5585
5586 /* XXX: this is just stupid */
5587 if (tag_ != 2)
5588 [install_ reloadData];
5589 if (tag_ != 3)
5590 [changes_ reloadData];
5591 if (tag_ != 5)
5592 [search_ reloadData];
5593
5594 [book_ reloadData];
5595 }
5596
5597 - (void) update_ {
5598 [database_ update];
5599 }
5600
5601 - (void) syncData {
5602 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5603 _assert(file != NULL);
5604
5605 NSArray *keys = [Sources_ allKeys];
5606
5607 for (int i(0), e([keys count]); i != e; ++i) {
5608 NSString *key = [keys objectAtIndex:i];
5609 NSDictionary *source = [Sources_ objectForKey:key];
5610
5611 fprintf(file, "%s %s %s\n",
5612 [[source objectForKey:@"Type"] UTF8String],
5613 [[source objectForKey:@"URI"] UTF8String],
5614 [[source objectForKey:@"Distribution"] UTF8String]
5615 );
5616 }
5617
5618 fclose(file);
5619
5620 [self _saveConfig];
5621
5622 [progress_
5623 detachNewThreadSelector:@selector(update_)
5624 toTarget:self
5625 withObject:nil
5626 title:@"Updating Sources..."
5627 ];
5628 }
5629
5630 - (void) reloadData {
5631 @synchronized (self) {
5632 if (confirm_ == nil)
5633 [self _reloadData];
5634 }
5635 }
5636
5637 - (void) resolve {
5638 pkgProblemResolver *resolver = [database_ resolver];
5639
5640 resolver->InstallProtect();
5641 if (!resolver->Resolve(true))
5642 _error->Discard();
5643 }
5644
5645 - (void) perform {
5646 [database_ prepare];
5647
5648 if ([database_ cache]->BrokenCount() == 0)
5649 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5650 else {
5651 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5652 NSArray *packages = [database_ packages];
5653
5654 for (size_t i(0); i != [packages count]; ++i) {
5655 Package *package = [packages objectAtIndex:i];
5656 if ([package broken])
5657 [broken addObject:[package name]];
5658 }
5659
5660 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5661 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5662 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5663 defaultButtonIndex:0
5664 delegate:self
5665 context:@"broken"
5666 ] autorelease];
5667
5668 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5669 [sheet popupAlertAnimated:YES];
5670
5671 [self _reloadData];
5672 }
5673 }
5674
5675 - (void) installPackage:(Package *)package {
5676 @synchronized (self) {
5677 [package install];
5678 [self resolve];
5679 [self perform];
5680 }
5681 }
5682
5683 - (void) removePackage:(Package *)package {
5684 @synchronized (self) {
5685 [package remove];
5686 [self resolve];
5687 [self perform];
5688 }
5689 }
5690
5691 - (void) distUpgrade {
5692 @synchronized (self) {
5693 [database_ upgrade];
5694 [self perform];
5695 }
5696 }
5697
5698 - (void) cancel {
5699 @synchronized (self) {
5700 [confirm_ release];
5701 confirm_ = nil;
5702 [self _reloadData];
5703 }
5704 }
5705
5706 - (void) confirm {
5707 [overlay_ removeFromSuperview];
5708 reload_ = true;
5709
5710 [progress_
5711 detachNewThreadSelector:@selector(perform)
5712 toTarget:database_
5713 withObject:nil
5714 title:@"Running..."
5715 ];
5716 }
5717
5718 - (void) bootstrap_ {
5719 [database_ update];
5720 [database_ upgrade];
5721 [database_ prepare];
5722 [database_ perform];
5723 }
5724
5725 - (void) bootstrap {
5726 [progress_
5727 detachNewThreadSelector:@selector(bootstrap_)
5728 toTarget:self
5729 withObject:nil
5730 title:@"Bootstrap Install..."
5731 ];
5732 }
5733
5734 - (void) progressViewIsComplete:(ProgressView *)progress {
5735 @synchronized (self) {
5736 [self _reloadData];
5737
5738 if (confirm_ != nil) {
5739 [underlay_ addSubview:overlay_];
5740 [confirm_ removeFromSuperview];
5741 [confirm_ release];
5742 confirm_ = nil;
5743 }
5744 }
5745 }
5746
5747 - (void) setPage:(RVPage *)page {
5748 [page resetViewAnimated:NO];
5749 [page setDelegate:self];
5750 [book_ setPage:page];
5751 }
5752
5753 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5754 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5755 [browser loadURL:url];
5756 return browser;
5757 }
5758
5759 - (void) _setHomePage {
5760 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5761 }
5762
5763 - (void) buttonBarItemTapped:(id)sender {
5764 unsigned tag = [sender tag];
5765 if (tag == tag_) {
5766 [book_ resetViewAnimated:YES];
5767 return;
5768 } else if (tag_ == 2 && tag != 2)
5769 [install_ resetView];
5770
5771 switch (tag) {
5772 case 1: [self _setHomePage]; break;
5773
5774 case 2: [self setPage:install_]; break;
5775 case 3: [self setPage:changes_]; break;
5776 case 4: [self setPage:manage_]; break;
5777 case 5: [self setPage:search_]; break;
5778
5779 default: _assert(false);
5780 }
5781
5782 tag_ = tag;
5783 }
5784
5785 - (void) fixSpringBoard {
5786 pid_t pid = ExecFork();
5787 if (pid == 0) {
5788 sleep(1);
5789
5790 if (pid_t child = fork()) {
5791 waitpid(child, NULL, 0);
5792 } else {
5793 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5794 perror("launchctl unload");
5795 exit(0);
5796 }
5797
5798 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5799 perror("launchctl load");
5800 exit(0);
5801 }
5802 }
5803
5804 - (void) applicationWillSuspend {
5805 [database_ clean];
5806
5807 if (reload_) {
5808 #ifndef __OBJC2__
5809 [self fixSpringBoard];
5810 #endif
5811 }
5812
5813 [super applicationWillSuspend];
5814 }
5815
5816 - (void) askForSettings {
5817 UIAlertSheet *role = [[[UIAlertSheet alloc]
5818 initWithTitle:@"Who Are You?"
5819 buttons:[NSArray arrayWithObjects:
5820 @"User (Graphical Only)",
5821 @"Hacker (+ Command Line)",
5822 @"Developer (No Filters)",
5823 nil]
5824 defaultButtonIndex:-1
5825 delegate:self
5826 context:@"role"
5827 ] autorelease];
5828
5829 [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."];
5830 [role popupAlertAnimated:YES];
5831 }
5832
5833 - (void) finish {
5834 if (hud_ != nil) {
5835 [self setStatusBarShowsProgress:NO];
5836
5837 [hud_ show:NO];
5838 [hud_ removeFromSuperview];
5839 [hud_ autorelease];
5840 hud_ = nil;
5841
5842 pid_t pid = ExecFork();
5843 if (pid == 0) {
5844 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5845 perror("launchctl stop");
5846 }
5847
5848 return;
5849 }
5850
5851 if (Role_ == nil) {
5852 [self askForSettings];
5853 return;
5854 }
5855
5856 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5857
5858 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5859 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5860 0, 0, screenrect.size.width, screenrect.size.height - 48
5861 ) database:database_];
5862
5863 [book_ setDelegate:self];
5864
5865 [overlay_ addSubview:book_];
5866
5867 NSArray *buttonitems = [NSArray arrayWithObjects:
5868 [NSDictionary dictionaryWithObjectsAndKeys:
5869 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5870 @"home-up.png", kUIButtonBarButtonInfo,
5871 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5872 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5873 self, kUIButtonBarButtonTarget,
5874 @"Home", kUIButtonBarButtonTitle,
5875 @"0", kUIButtonBarButtonType,
5876 nil],
5877
5878 [NSDictionary dictionaryWithObjectsAndKeys:
5879 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5880 @"install-up.png", kUIButtonBarButtonInfo,
5881 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5882 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5883 self, kUIButtonBarButtonTarget,
5884 @"Sections", kUIButtonBarButtonTitle,
5885 @"0", kUIButtonBarButtonType,
5886 nil],
5887
5888 [NSDictionary dictionaryWithObjectsAndKeys:
5889 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5890 @"changes-up.png", kUIButtonBarButtonInfo,
5891 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5892 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5893 self, kUIButtonBarButtonTarget,
5894 @"Changes", kUIButtonBarButtonTitle,
5895 @"0", kUIButtonBarButtonType,
5896 nil],
5897
5898 [NSDictionary dictionaryWithObjectsAndKeys:
5899 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5900 @"manage-up.png", kUIButtonBarButtonInfo,
5901 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5902 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5903 self, kUIButtonBarButtonTarget,
5904 @"Manage", kUIButtonBarButtonTitle,
5905 @"0", kUIButtonBarButtonType,
5906 nil],
5907
5908 [NSDictionary dictionaryWithObjectsAndKeys:
5909 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5910 @"search-up.png", kUIButtonBarButtonInfo,
5911 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5912 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5913 self, kUIButtonBarButtonTarget,
5914 @"Search", kUIButtonBarButtonTitle,
5915 @"0", kUIButtonBarButtonType,
5916 nil],
5917 nil];
5918
5919 buttonbar_ = [[UIButtonBar alloc]
5920 initInView:overlay_
5921 withFrame:CGRectMake(
5922 0, screenrect.size.height - ButtonBarHeight_,
5923 screenrect.size.width, ButtonBarHeight_
5924 )
5925 withItemList:buttonitems
5926 ];
5927
5928 [buttonbar_ setDelegate:self];
5929 [buttonbar_ setBarStyle:1];
5930 [buttonbar_ setButtonBarTrackingMode:2];
5931
5932 int buttons[5] = {1, 2, 3, 4, 5};
5933 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5934 [buttonbar_ showButtonGroup:0 withDuration:0];
5935
5936 for (int i = 0; i != 5; ++i)
5937 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5938 i * 64 + 2, 1, 60, ButtonBarHeight_
5939 )];
5940
5941 [buttonbar_ showSelectionForButton:1];
5942 [overlay_ addSubview:buttonbar_];
5943
5944 [UIKeyboard initImplementationNow];
5945 CGSize keysize = [UIKeyboard defaultSize];
5946 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5947 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5948 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5949 [overlay_ addSubview:keyboard_];
5950
5951 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5952 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5953 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5954
5955 manage_ = (ManageView *) [[self
5956 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5957 withClass:[ManageView class]
5958 ] retain];
5959
5960 if (!bootstrap_)
5961 [underlay_ addSubview:overlay_];
5962
5963 [self reloadData];
5964
5965 if (bootstrap_)
5966 [self bootstrap];
5967 else
5968 [self _setHomePage];
5969 }
5970
5971 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5972 NSString *context = [sheet context];
5973 if ([context isEqualToString:@"fixhalf"])
5974 switch (button) {
5975 case 1:
5976 @synchronized (self) {
5977 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5978 Package *broken = [broken_ objectAtIndex:i];
5979 [broken remove];
5980
5981 NSString *id = [broken id];
5982 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5983 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5984 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5985 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5986 }
5987
5988 [self resolve];
5989 [self perform];
5990 }
5991 break;
5992
5993 case 2:
5994 [broken_ removeAllObjects];
5995 [self _loaded];
5996 break;
5997
5998 default:
5999 _assert(false);
6000 }
6001 else if ([context isEqualToString:@"role"]) {
6002 switch (button) {
6003 case 1: Role_ = @"User"; break;
6004 case 2: Role_ = @"Hacker"; break;
6005 case 3: Role_ = @"Developer"; break;
6006
6007 default:
6008 Role_ = nil;
6009 _assert(false);
6010 }
6011
6012 bool reset = Settings_ != nil;
6013
6014 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6015 Role_, @"Role",
6016 nil];
6017
6018 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6019
6020 Changed_ = true;
6021
6022 if (reset)
6023 [self updateData];
6024 else
6025 [self finish];
6026 } else if ([context isEqualToString:@"upgrade"])
6027 switch (button) {
6028 case 1:
6029 @synchronized (self) {
6030 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6031 Package *essential = [essential_ objectAtIndex:i];
6032 [essential install];
6033 }
6034
6035 [self resolve];
6036 [self perform];
6037 }
6038 break;
6039
6040 case 2:
6041 Ignored_ = YES;
6042 break;
6043
6044 default:
6045 _assert(false);
6046 }
6047
6048 [sheet dismiss];
6049 }
6050
6051 - (void) reorganize {
6052 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6053 system("/usr/libexec/cydia/free.sh");
6054 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6055 [pool release];
6056 }
6057
6058 - (void) applicationSuspend:(__GSEvent *)event {
6059 if (hud_ == nil && ![progress_ isRunning])
6060 [super applicationSuspend:event];
6061 }
6062
6063 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6064 if (hud_ == nil)
6065 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6066 }
6067
6068 - (void) _setSuspended:(BOOL)value {
6069 if (hud_ == nil)
6070 [super _setSuspended:value];
6071 }
6072
6073 - (UIProgressHUD *) addProgressHUD {
6074 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6075 [hud show:YES];
6076 [underlay_ addSubview:hud];
6077 return hud;
6078 }
6079
6080 - (void) applicationDidFinishLaunching:(id)unused {
6081 Font12_ = [[UIFont systemFontOfSize:12] retain];
6082 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6083 Font14_ = [[UIFont systemFontOfSize:14] retain];
6084 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6085 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6086
6087 _assert(pkgInitConfig(*_config));
6088 _assert(pkgInitSystem(*_config, _system));
6089
6090 confirm_ = nil;
6091 tag_ = 1;
6092
6093 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6094 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6095
6096 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6097 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6098
6099 [window_ orderFront:self];
6100 [window_ makeKey:self];
6101 [window_ _setHidden:NO];
6102
6103 database_ = [[Database alloc] init];
6104 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6105 [database_ setDelegate:progress_];
6106 [window_ setContentView:progress_];
6107
6108 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6109 [progress_ setContentView:underlay_];
6110
6111 [progress_ resetView];
6112
6113 if (
6114 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6115 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6116 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6117 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6118 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6119 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
6120 ) {
6121 [self setIdleTimerDisabled:YES];
6122
6123 hud_ = [self addProgressHUD];
6124 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6125
6126 [self setStatusBarShowsProgress:YES];
6127
6128 [NSThread
6129 detachNewThreadSelector:@selector(reorganize)
6130 toTarget:self
6131 withObject:nil
6132 ];
6133 } else
6134 [self finish];
6135 }
6136
6137 - (void) showKeyboard:(BOOL)show {
6138 CGSize keysize = [UIKeyboard defaultSize];
6139 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6140 CGRect keyup = keydown;
6141 keyup.origin.y -= keysize.height;
6142
6143 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6144 [animation setSignificantRectFields:2];
6145
6146 if (show) {
6147 [animation setStartFrame:keydown];
6148 [animation setEndFrame:keyup];
6149 [keyboard_ activate];
6150 } else {
6151 [animation setStartFrame:keyup];
6152 [animation setEndFrame:keydown];
6153 [keyboard_ deactivate];
6154 }
6155
6156 [[UIAnimator sharedAnimator]
6157 addAnimations:[NSArray arrayWithObjects:animation, nil]
6158 withDuration:KeyboardTime_
6159 start:YES
6160 ];
6161 }
6162
6163 - (void) slideUp:(UIAlertSheet *)alert {
6164 if (Advanced_)
6165 [alert presentSheetFromButtonBar:buttonbar_];
6166 else
6167 [alert presentSheetInView:overlay_];
6168 }
6169
6170 @end
6171
6172 void AddPreferences(NSString *plist) {
6173 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6174
6175 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6176 _assert(settings != NULL);
6177 NSMutableArray *items = [settings objectForKey:@"items"];
6178
6179 bool cydia(false);
6180
6181 for (size_t i(0); i != [items count]; ++i) {
6182 NSMutableDictionary *item([items objectAtIndex:i]);
6183 NSString *label = [item objectForKey:@"label"];
6184 if (label != nil && [label isEqualToString:@"Cydia"]) {
6185 cydia = true;
6186 break;
6187 }
6188 }
6189
6190 if (!cydia) {
6191 for (size_t i(0); i != [items count]; ++i) {
6192 NSDictionary *item([items objectAtIndex:i]);
6193 NSString *label = [item objectForKey:@"label"];
6194 if (label != nil && [label isEqualToString:@"General"]) {
6195 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6196 @"CydiaSettings", @"bundle",
6197 @"PSLinkCell", @"cell",
6198 [NSNumber numberWithBool:YES], @"hasIcon",
6199 [NSNumber numberWithBool:YES], @"isController",
6200 @"Cydia", @"label",
6201 nil] atIndex:(i + 1)];
6202
6203 break;
6204 }
6205 }
6206
6207 _assert([settings writeToFile:plist atomically:YES] == YES);
6208 }
6209
6210 [pool release];
6211 }
6212
6213 /*IMP alloc_;
6214 id Alloc_(id self, SEL selector) {
6215 id object = alloc_(self, selector);
6216 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6217 return object;
6218 }*/
6219
6220 /*IMP dealloc_;
6221 id Dealloc_(id self, SEL selector) {
6222 id object = dealloc_(self, selector);
6223 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6224 return object;
6225 }*/
6226
6227 int main(int argc, char *argv[]) {
6228 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6229
6230 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6231
6232 Home_ = NSHomeDirectory();
6233
6234 {
6235 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6236 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6237 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6238 Sounds_Keyboard_ = [keyboard boolValue];
6239 }
6240
6241 setuid(0);
6242 setgid(0);
6243
6244 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6245 _assert(errno == ENOENT);
6246 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6247 _assert(errno == ENOENT);
6248
6249 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6250 alloc_ = alloc->method_imp;
6251 alloc->method_imp = (IMP) &Alloc_;*/
6252
6253 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6254 dealloc_ = dealloc->method_imp;
6255 dealloc->method_imp = (IMP) &Dealloc_;*/
6256
6257 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6258 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6259 Firmware_ = strdup([prover UTF8String]);
6260 NSArray *versions = [prover componentsSeparatedByString:@"."];
6261 int count = [versions count];
6262 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6263 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6264 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6265 }
6266 }
6267
6268 size_t size;
6269 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6270 char *machine = new char[size];
6271 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6272 Machine_ = machine;
6273
6274 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6275 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6276 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6277 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6278 CFRelease(serial);
6279 }
6280
6281 IOObjectRelease(service);
6282 }
6283
6284 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6285 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6286
6287 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6288 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6289 else {
6290 Settings_ = [Metadata_ objectForKey:@"Settings"];
6291
6292 Packages_ = [Metadata_ objectForKey:@"Packages"];
6293 Sections_ = [Metadata_ objectForKey:@"Sections"];
6294 Sources_ = [Metadata_ objectForKey:@"Sources"];
6295 }
6296
6297 if (Settings_ != nil)
6298 Role_ = [Settings_ objectForKey:@"Role"];
6299
6300 if (Packages_ == nil) {
6301 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6302 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6303 }
6304
6305 if (Sections_ == nil) {
6306 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6307 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6308 }
6309
6310 if (Sources_ == nil) {
6311 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6312 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6313 }
6314
6315 if (access("/User", F_OK) != 0)
6316 system("/usr/libexec/cydia/firmware.sh");
6317
6318 Locale_ = CFLocaleCopyCurrent();
6319 space_ = CGColorSpaceCreateDeviceRGB();
6320
6321 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6322 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6323 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6324 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6325 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6326 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6327 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6328
6329 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6330
6331 int value = UIApplicationMain(argc, argv, [Cydia class]);
6332
6333 CGColorSpaceRelease(space_);
6334 CFRelease(Locale_);
6335
6336 [pool release];
6337 return value;
6338 }