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