]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Finished implementing Storage.
[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 /* Manage View {{{ */
4759 @interface ManageView : BrowserView {
4760 }
4761
4762 @end
4763
4764 @implementation ManageView
4765
4766 - (NSString *) title {
4767 return @"Manage";
4768 }
4769
4770 - (void) _leftButtonClicked {
4771 [delegate_ askForSettings];
4772 }
4773
4774 - (NSString *) leftButtonTitle {
4775 return @"Settings";
4776 }
4777
4778 #if !AlwaysReload
4779 - (id) _rightButtonTitle {
4780 return nil;
4781 }
4782 #endif
4783
4784 - (bool) _loading {
4785 return false;
4786 }
4787
4788 @end
4789 /* }}} */
4790
4791 /* Indirect Delegate {{{ */
4792 @interface IndirectDelegate : NSProxy {
4793 _transient volatile id delegate_;
4794 }
4795
4796 - (void) setDelegate:(id)delegate;
4797 - (id) initWithDelegate:(id)delegate;
4798 @end
4799
4800 @implementation IndirectDelegate
4801
4802 - (void) setDelegate:(id)delegate {
4803 delegate_ = delegate;
4804 }
4805
4806 - (id) initWithDelegate:(id)delegate {
4807 delegate_ = delegate;
4808 return self;
4809 }
4810
4811 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4812 if (delegate_ != nil)
4813 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4814 return sig;
4815 // XXX: I fucking hate Apple so very very bad
4816 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
4817 }
4818
4819 - (void) forwardInvocation:(NSInvocation *)inv {
4820 SEL sel = [inv selector];
4821 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4822 [inv invokeWithTarget:delegate_];
4823 }
4824
4825 @end
4826 /* }}} */
4827
4828 #include <BrowserView.m>
4829
4830 /* Cydia Book {{{ */
4831 @interface CYBook : RVBook <
4832 ProgressDelegate
4833 > {
4834 _transient Database *database_;
4835 UINavigationBar *overlay_;
4836 UINavigationBar *underlay_;
4837 UIProgressIndicator *indicator_;
4838 UITextLabel *prompt_;
4839 UIProgressBar *progress_;
4840 UINavigationButton *cancel_;
4841 bool updating_;
4842 size_t received_;
4843 NSTimeInterval last_;
4844 }
4845
4846 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4847 - (void) update;
4848 - (BOOL) updating;
4849
4850 @end
4851
4852 @implementation CYBook
4853
4854 - (void) dealloc {
4855 [overlay_ release];
4856 [indicator_ release];
4857 [prompt_ release];
4858 [progress_ release];
4859 [cancel_ release];
4860 [super dealloc];
4861 }
4862
4863 - (NSString *) getTitleForPage:(RVPage *)page {
4864 return Simplify([super getTitleForPage:page]);
4865 }
4866
4867 - (BOOL) updating {
4868 return updating_;
4869 }
4870
4871 - (void) update {
4872 [UIView beginAnimations:nil context:NULL];
4873
4874 CGRect ovrframe = [overlay_ frame];
4875 ovrframe.origin.y = 0;
4876 [overlay_ setFrame:ovrframe];
4877
4878 CGRect barframe = [navbar_ frame];
4879 barframe.origin.y += ovrframe.size.height;
4880 [navbar_ setFrame:barframe];
4881
4882 CGRect trnframe = [transition_ frame];
4883 trnframe.origin.y += ovrframe.size.height;
4884 trnframe.size.height -= ovrframe.size.height;
4885 [transition_ setFrame:trnframe];
4886
4887 [UIView endAnimations];
4888
4889 [indicator_ startAnimation];
4890 [prompt_ setText:@"Updating Database"];
4891 [progress_ setProgress:0];
4892
4893 received_ = 0;
4894 last_ = [NSDate timeIntervalSinceReferenceDate];
4895 updating_ = true;
4896 [overlay_ addSubview:cancel_];
4897
4898 [NSThread
4899 detachNewThreadSelector:@selector(_update)
4900 toTarget:self
4901 withObject:nil
4902 ];
4903 }
4904
4905 - (void) _update_ {
4906 updating_ = false;
4907
4908 [indicator_ stopAnimation];
4909
4910 [UIView beginAnimations:nil context:NULL];
4911
4912 CGRect ovrframe = [overlay_ frame];
4913 ovrframe.origin.y = -ovrframe.size.height;
4914 [overlay_ setFrame:ovrframe];
4915
4916 CGRect barframe = [navbar_ frame];
4917 barframe.origin.y -= ovrframe.size.height;
4918 [navbar_ setFrame:barframe];
4919
4920 CGRect trnframe = [transition_ frame];
4921 trnframe.origin.y -= ovrframe.size.height;
4922 trnframe.size.height += ovrframe.size.height;
4923 [transition_ setFrame:trnframe];
4924
4925 [UIView commitAnimations];
4926
4927 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
4928 }
4929
4930 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4931 if ((self = [super initWithFrame:frame]) != nil) {
4932 database_ = database;
4933
4934 CGRect ovrrect = [navbar_ bounds];
4935 ovrrect.size.height = [UINavigationBar defaultSize].height;
4936 ovrrect.origin.y = -ovrrect.size.height;
4937
4938 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4939 [self addSubview:overlay_];
4940
4941 ovrrect.origin.y = frame.size.height;
4942 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
4943 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
4944 [self addSubview:underlay_];
4945
4946 [overlay_ setBarStyle:1];
4947 [underlay_ setBarStyle:1];
4948
4949 int barstyle = [overlay_ _barStyle:NO];
4950 bool ugly = barstyle == 0;
4951
4952 UIProgressIndicatorStyle style = ugly ?
4953 UIProgressIndicatorStyleMediumBrown :
4954 UIProgressIndicatorStyleMediumWhite;
4955
4956 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4957 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4958 CGRect indrect = {{indoffset, indoffset}, indsize};
4959
4960 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4961 [indicator_ setStyle:style];
4962 [overlay_ addSubview:indicator_];
4963
4964 CGSize prmsize = {215, indsize.height + 4};
4965
4966 CGRect prmrect = {{
4967 indoffset * 2 + indsize.width,
4968 #ifdef __OBJC2__
4969 -1 +
4970 #endif
4971 unsigned(ovrrect.size.height - prmsize.height) / 2
4972 }, prmsize};
4973
4974 UIFont *font = [UIFont systemFontOfSize:15];
4975
4976 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4977
4978 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
4979 [prompt_ setBackgroundColor:[UIColor clearColor]];
4980 [prompt_ setFont:font];
4981
4982 [overlay_ addSubview:prompt_];
4983
4984 CGSize prgsize = {75, 100};
4985
4986 CGRect prgrect = {{
4987 ovrrect.size.width - prgsize.width - 10,
4988 (ovrrect.size.height - prgsize.height) / 2
4989 } , prgsize};
4990
4991 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4992 [progress_ setStyle:0];
4993 [overlay_ addSubview:progress_];
4994
4995 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
4996 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
4997
4998 CGRect frame = [cancel_ frame];
4999 frame.size.width = 65;
5000 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5001 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5002 [cancel_ setFrame:frame];
5003
5004 [cancel_ setBarStyle:barstyle];
5005 } return self;
5006 }
5007
5008 - (void) _onCancel {
5009 updating_ = false;
5010 [cancel_ removeFromSuperview];
5011 }
5012
5013 - (void) _update { _pooled
5014 Status status;
5015 status.setDelegate(self);
5016
5017 [database_ updateWithStatus:status];
5018
5019 [self
5020 performSelectorOnMainThread:@selector(_update_)
5021 withObject:nil
5022 waitUntilDone:NO
5023 ];
5024 }
5025
5026 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5027 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5028 }
5029
5030 - (void) setProgressTitle:(NSString *)title {
5031 [self
5032 performSelectorOnMainThread:@selector(_setProgressTitle:)
5033 withObject:title
5034 waitUntilDone:YES
5035 ];
5036 }
5037
5038 - (void) setProgressPercent:(float)percent {
5039 [self
5040 performSelectorOnMainThread:@selector(_setProgressPercent:)
5041 withObject:[NSNumber numberWithFloat:percent]
5042 waitUntilDone:YES
5043 ];
5044 }
5045
5046 - (void) startProgress {
5047 }
5048
5049 - (void) addProgressOutput:(NSString *)output {
5050 [self
5051 performSelectorOnMainThread:@selector(_addProgressOutput:)
5052 withObject:output
5053 waitUntilDone:YES
5054 ];
5055 }
5056
5057 - (bool) isCancelling:(size_t)received {
5058 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5059 if (received_ != received) {
5060 received_ = received;
5061 last_ = now;
5062 } else if (now - last_ > 15)
5063 return true;
5064 return !updating_;
5065 }
5066
5067 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5068 [sheet dismiss];
5069 }
5070
5071 - (void) _setProgressTitle:(NSString *)title {
5072 [prompt_ setText:title];
5073 }
5074
5075 - (void) _setProgressPercent:(NSNumber *)percent {
5076 [progress_ setProgress:[percent floatValue]];
5077 }
5078
5079 - (void) _addProgressOutput:(NSString *)output {
5080 }
5081
5082 @end
5083 /* }}} */
5084 /* Cydia:// Protocol {{{ */
5085 @interface CydiaURLProtocol : NSURLProtocol {
5086 }
5087
5088 @end
5089
5090 @implementation CydiaURLProtocol
5091
5092 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5093 NSURL *url([request URL]);
5094 if (url == nil)
5095 return NO;
5096 NSString *scheme([[url scheme] lowercaseString]);
5097 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5098 return NO;
5099 return YES;
5100 }
5101
5102 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5103 return request;
5104 }
5105
5106 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5107 id<NSURLProtocolClient> client([self client]);
5108 NSData *data(UIImagePNGRepresentation(icon));
5109
5110 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5111 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5112 [client URLProtocol:self didLoadData:data];
5113 [client URLProtocolDidFinishLoading:self];
5114 }
5115
5116 - (void) startLoading {
5117 id<NSURLProtocolClient> client([self client]);
5118 NSURLRequest *request([self request]);
5119
5120 NSURL *url([request URL]);
5121 NSString *href([url absoluteString]);
5122
5123 NSString *path([href substringFromIndex:8]);
5124 NSRange slash([path rangeOfString:@"/"]);
5125
5126 NSString *command;
5127 if (slash.location == NSNotFound) {
5128 command = path;
5129 path = nil;
5130 } else {
5131 command = [path substringToIndex:slash.location];
5132 path = [path substringFromIndex:(slash.location + 1)];
5133 }
5134
5135 Database *database([Database sharedInstance]);
5136
5137 if ([command isEqualToString:@"package-icon"]) {
5138 if (path == nil)
5139 goto fail;
5140 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5141 Package *package([database packageWithName:path]);
5142 if (package == nil)
5143 goto fail;
5144 UIImage *icon([package icon]);
5145 [self _returnPNGWithImage:icon forRequest:request];
5146 } else if ([command isEqualToString:@"source-icon"]) {
5147 if (path == nil)
5148 goto fail;
5149 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5150 NSString *source(Simplify(path));
5151 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5152 if (icon == nil)
5153 icon = [UIImage applicationImageNamed:@"unknown.png"];
5154 [self _returnPNGWithImage:icon forRequest:request];
5155 } else if ([command isEqualToString:@"uikit-image"]) {
5156 if (path == nil)
5157 goto fail;
5158 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5159 UIImage *icon(_UIImageWithName(path));
5160 [self _returnPNGWithImage:icon forRequest:request];
5161 } else if ([command isEqualToString:@"section-icon"]) {
5162 if (path == nil)
5163 goto fail;
5164 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5165 NSString *section(Simplify(path));
5166 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5167 if (icon == nil)
5168 icon = [UIImage applicationImageNamed:@"unknown.png"];
5169 [self _returnPNGWithImage:icon forRequest:request];
5170 } else fail: {
5171 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5172 }
5173 }
5174
5175 - (void) stopLoading {
5176 }
5177
5178 @end
5179 /* }}} */
5180
5181 /* Install View {{{ */
5182 @interface InstallView : RVPage {
5183 _transient Database *database_;
5184 NSMutableArray *sections_;
5185 NSMutableArray *filtered_;
5186 UITransitionView *transition_;
5187 UITable *list_;
5188 UIView *accessory_;
5189 BOOL editing_;
5190 }
5191
5192 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5193 - (void) reloadData;
5194 - (void) resetView;
5195
5196 @end
5197
5198 @implementation InstallView
5199
5200 - (void) dealloc {
5201 [list_ setDataSource:nil];
5202 [list_ setDelegate:nil];
5203
5204 [sections_ release];
5205 [filtered_ release];
5206 [transition_ release];
5207 [list_ release];
5208 [accessory_ release];
5209 [super dealloc];
5210 }
5211
5212 - (int) numberOfRowsInTable:(UITable *)table {
5213 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5214 }
5215
5216 - (float) table:(UITable *)table heightForRow:(int)row {
5217 return 45;
5218 }
5219
5220 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5221 if (reusing == nil)
5222 reusing = [[[SectionCell alloc] init] autorelease];
5223 [(SectionCell *)reusing setSection:(editing_ ?
5224 [sections_ objectAtIndex:row] :
5225 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5226 ) editing:editing_];
5227 return reusing;
5228 }
5229
5230 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5231 return !editing_;
5232 }
5233
5234 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5235 return !editing_;
5236 }
5237
5238 - (void) tableRowSelected:(NSNotification *)notification {
5239 int row = [[notification object] selectedRow];
5240 if (row == INT_MAX)
5241 return;
5242
5243 Section *section;
5244 NSString *name;
5245 NSString *title;
5246
5247 if (row == 0) {
5248 section = nil;
5249 name = nil;
5250 title = @"All Packages";
5251 } else {
5252 section = [filtered_ objectAtIndex:(row - 1)];
5253 name = [section name];
5254
5255 if (name != nil)
5256 title = name;
5257 else {
5258 name = @"";
5259 title = @"(No Section)";
5260 }
5261 }
5262
5263 PackageTable *table = [[[PackageTable alloc]
5264 initWithBook:book_
5265 database:database_
5266 title:title
5267 filter:@selector(isVisiblyUninstalledInSection:)
5268 with:name
5269 ] autorelease];
5270
5271 [table setDelegate:delegate_];
5272
5273 [book_ pushPage:table];
5274 }
5275
5276 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5277 if ((self = [super initWithBook:book]) != nil) {
5278 database_ = database;
5279
5280 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5281 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5282
5283 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5284 [self addSubview:transition_];
5285
5286 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5287 [transition_ transition:0 toView:list_];
5288
5289 UITableColumn *column = [[[UITableColumn alloc]
5290 initWithTitle:@"Name"
5291 identifier:@"name"
5292 width:[self frame].size.width
5293 ] autorelease];
5294
5295 [list_ setDataSource:self];
5296 [list_ setSeparatorStyle:1];
5297 [list_ addTableColumn:column];
5298 [list_ setDelegate:self];
5299 [list_ setReusesTableCells:YES];
5300
5301 [self reloadData];
5302
5303 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5304 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5305 } return self;
5306 }
5307
5308 - (void) reloadData {
5309 NSArray *packages = [database_ packages];
5310
5311 [sections_ removeAllObjects];
5312 [filtered_ removeAllObjects];
5313
5314 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5315 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5316
5317 _trace();
5318 for (size_t i(0); i != [packages count]; ++i) {
5319 Package *package([packages objectAtIndex:i]);
5320 NSString *name([package section]);
5321
5322 if (name != nil) {
5323 Section *section([sections objectForKey:name]);
5324 if (section == nil) {
5325 section = [[[Section alloc] initWithName:name] autorelease];
5326 [sections setObject:section forKey:name];
5327 }
5328 }
5329
5330 if ([package valid] && [package installed] == nil && [package visible])
5331 [filtered addObject:package];
5332 }
5333 _trace();
5334
5335 [sections_ addObjectsFromArray:[sections allValues]];
5336 [sections_ sortUsingSelector:@selector(compareByName:)];
5337
5338 _trace();
5339 [filtered sortUsingSelector:@selector(compareBySection:)];
5340 _trace();
5341
5342 Section *section = nil;
5343 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5344 Package *package = [filtered objectAtIndex:offset];
5345 NSString *name = [package section];
5346
5347 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5348 section = name == nil ?
5349 [[[Section alloc] initWithName:nil] autorelease] :
5350 [sections objectForKey:name];
5351 [filtered_ addObject:section];
5352 }
5353
5354 [section addToCount];
5355 }
5356 _trace();
5357
5358 [list_ reloadData];
5359 _trace();
5360 }
5361
5362 - (void) resetView {
5363 if (editing_)
5364 [self _rightButtonClicked];
5365 }
5366
5367 - (void) resetViewAnimated:(BOOL)animated {
5368 [list_ resetViewAnimated:animated];
5369 }
5370
5371 - (void) _rightButtonClicked {
5372 if ((editing_ = !editing_))
5373 [list_ reloadData];
5374 else
5375 [delegate_ updateData];
5376 [book_ reloadTitleForPage:self];
5377 [book_ reloadButtonsForPage:self];
5378 }
5379
5380 - (NSString *) title {
5381 return editing_ ? @"Section Visibility" : @"Install by Section";
5382 }
5383
5384 - (NSString *) backButtonTitle {
5385 return @"Sections";
5386 }
5387
5388 - (id) rightButtonTitle {
5389 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5390 }
5391
5392 - (UINavigationButtonStyle) rightButtonStyle {
5393 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5394 }
5395
5396 - (UIView *) accessoryView {
5397 return accessory_;
5398 }
5399
5400 @end
5401 /* }}} */
5402 /* Changes View {{{ */
5403 @interface ChangesView : RVPage {
5404 _transient Database *database_;
5405 NSMutableArray *packages_;
5406 NSMutableArray *sections_;
5407 UISectionList *list_;
5408 unsigned upgrades_;
5409 }
5410
5411 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5412 - (void) reloadData;
5413
5414 @end
5415
5416 @implementation ChangesView
5417
5418 - (void) dealloc {
5419 [[list_ table] setDelegate:nil];
5420 [list_ setDataSource:nil];
5421
5422 [packages_ release];
5423 [sections_ release];
5424 [list_ release];
5425 [super dealloc];
5426 }
5427
5428 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5429 return [sections_ count];
5430 }
5431
5432 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5433 return [[sections_ objectAtIndex:section] name];
5434 }
5435
5436 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5437 return [[sections_ objectAtIndex:section] row];
5438 }
5439
5440 - (int) numberOfRowsInTable:(UITable *)table {
5441 return [packages_ count];
5442 }
5443
5444 - (float) table:(UITable *)table heightForRow:(int)row {
5445 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5446 }
5447
5448 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5449 if (reusing == nil)
5450 reusing = [[[PackageCell alloc] init] autorelease];
5451 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5452 return reusing;
5453 }
5454
5455 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5456 return NO;
5457 }
5458
5459 - (void) tableRowSelected:(NSNotification *)notification {
5460 int row = [[notification object] selectedRow];
5461 if (row == INT_MAX)
5462 return;
5463 Package *package = [packages_ objectAtIndex:row];
5464 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5465 [view setDelegate:delegate_];
5466 [view setPackage:package];
5467 [book_ pushPage:view];
5468 }
5469
5470 - (void) _leftButtonClicked {
5471 [(CYBook *)book_ update];
5472 [self reloadButtons];
5473 }
5474
5475 - (void) _rightButtonClicked {
5476 [delegate_ distUpgrade];
5477 }
5478
5479 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5480 if ((self = [super initWithBook:book]) != nil) {
5481 database_ = database;
5482
5483 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5484 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5485
5486 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5487 [self addSubview:list_];
5488
5489 [list_ setShouldHideHeaderInShortLists:NO];
5490 [list_ setDataSource:self];
5491 //[list_ setSectionListStyle:1];
5492
5493 UITableColumn *column = [[[UITableColumn alloc]
5494 initWithTitle:@"Name"
5495 identifier:@"name"
5496 width:[self frame].size.width
5497 ] autorelease];
5498
5499 UITable *table = [list_ table];
5500 [table setSeparatorStyle:1];
5501 [table addTableColumn:column];
5502 [table setDelegate:self];
5503 [table setReusesTableCells:YES];
5504
5505 [self reloadData];
5506
5507 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5508 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5509 } return self;
5510 }
5511
5512 - (void) reloadData {
5513 NSArray *packages = [database_ packages];
5514
5515 [packages_ removeAllObjects];
5516 [sections_ removeAllObjects];
5517
5518 _trace();
5519 for (size_t i(0); i != [packages count]; ++i) {
5520 Package *package([packages objectAtIndex:i]);
5521
5522 if (
5523 [package installed] == nil && [package valid] && [package visible] ||
5524 [package upgradableAndEssential:NO]
5525 )
5526 [packages_ addObject:package];
5527 }
5528
5529 _trace();
5530 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5531 _trace();
5532
5533 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5534 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5535 Section *section = nil;
5536 NSDate *last = nil;
5537
5538 upgrades_ = 0;
5539 bool unseens = false;
5540
5541 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5542
5543 _trace();
5544 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5545 Package *package = [packages_ objectAtIndex:offset];
5546
5547 if (![package upgradableAndEssential:YES]) {
5548 unseens = true;
5549 NSDate *seen = [package seen];
5550
5551 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5552 last = seen;
5553
5554 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5555 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5556 [sections_ addObject:section];
5557 [name release];
5558 }
5559
5560 [section addToCount];
5561 } else if ([package ignored])
5562 [ignored addToCount];
5563 else {
5564 ++upgrades_;
5565 [upgradable addToCount];
5566 }
5567 }
5568 _trace();
5569
5570 CFRelease(formatter);
5571
5572 if (unseens) {
5573 Section *last = [sections_ lastObject];
5574 size_t count = [last count];
5575 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5576 [sections_ removeLastObject];
5577 }
5578
5579 if ([ignored count] != 0)
5580 [sections_ insertObject:ignored atIndex:0];
5581 if (upgrades_ != 0)
5582 [sections_ insertObject:upgradable atIndex:0];
5583
5584 [list_ reloadData];
5585 [self reloadButtons];
5586 }
5587
5588 - (void) resetViewAnimated:(BOOL)animated {
5589 [list_ resetViewAnimated:animated];
5590 }
5591
5592 - (NSString *) leftButtonTitle {
5593 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5594 }
5595
5596 - (id) rightButtonTitle {
5597 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5598 }
5599
5600 - (NSString *) title {
5601 return @"Changes";
5602 }
5603
5604 @end
5605 /* }}} */
5606 /* Search View {{{ */
5607 @protocol SearchViewDelegate
5608 - (void) showKeyboard:(BOOL)show;
5609 @end
5610
5611 @interface SearchView : RVPage {
5612 UIView *accessory_;
5613 UISearchField *field_;
5614 UITransitionView *transition_;
5615 PackageTable *table_;
5616 UIPreferencesTable *advanced_;
5617 UIView *dimmed_;
5618 bool flipped_;
5619 bool reload_;
5620 }
5621
5622 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5623 - (void) reloadData;
5624
5625 @end
5626
5627 @implementation SearchView
5628
5629 - (void) dealloc {
5630 [field_ setDelegate:nil];
5631
5632 [accessory_ release];
5633 [field_ release];
5634 [transition_ release];
5635 [table_ release];
5636 [advanced_ release];
5637 [dimmed_ release];
5638 [super dealloc];
5639 }
5640
5641 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5642 return 1;
5643 }
5644
5645 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5646 switch (group) {
5647 case 0: return @"Advanced Search (Coming Soon!)";
5648
5649 default: _assert(false);
5650 }
5651 }
5652
5653 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5654 switch (group) {
5655 case 0: return 0;
5656
5657 default: _assert(false);
5658 }
5659 }
5660
5661 - (void) _showKeyboard:(BOOL)show {
5662 CGSize keysize = [UIKeyboard defaultSize];
5663 CGRect keydown = [book_ pageBounds];
5664 CGRect keyup = keydown;
5665 keyup.size.height -= keysize.height - ButtonBarHeight_;
5666
5667 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5668
5669 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5670 [animation setSignificantRectFields:8];
5671
5672 if (show) {
5673 [animation setStartFrame:keydown];
5674 [animation setEndFrame:keyup];
5675 } else {
5676 [animation setStartFrame:keyup];
5677 [animation setEndFrame:keydown];
5678 }
5679
5680 UIAnimator *animator = [UIAnimator sharedAnimator];
5681
5682 [animator
5683 addAnimations:[NSArray arrayWithObjects:animation, nil]
5684 withDuration:(KeyboardTime_ - delay)
5685 start:!show
5686 ];
5687
5688 if (show)
5689 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5690
5691 [delegate_ showKeyboard:show];
5692 }
5693
5694 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5695 [self _showKeyboard:YES];
5696 }
5697
5698 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5699 [self _showKeyboard:NO];
5700 }
5701
5702 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5703 if (reload_) {
5704 NSString *text([field_ text]);
5705 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5706 [self reloadData];
5707 reload_ = false;
5708 }
5709 }
5710
5711 - (void) textFieldClearButtonPressed:(UITextField *)field {
5712 reload_ = true;
5713 }
5714
5715 - (void) keyboardInputShouldDelete:(id)input {
5716 reload_ = true;
5717 }
5718
5719 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5720 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5721 reload_ = true;
5722 return YES;
5723 } else {
5724 [field_ resignFirstResponder];
5725 return NO;
5726 }
5727 }
5728
5729 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5730 if ((self = [super initWithBook:book]) != nil) {
5731 CGRect pageBounds = [book_ pageBounds];
5732
5733 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5734 [self addSubview:transition_];
5735
5736 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5737
5738 [advanced_ setReusesTableCells:YES];
5739 [advanced_ setDataSource:self];
5740 [advanced_ reloadData];
5741
5742 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5743 CGColor dimmed(space_, 0, 0, 0, 0.5);
5744 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
5745
5746 table_ = [[PackageTable alloc]
5747 initWithBook:book
5748 database:database
5749 title:nil
5750 filter:@selector(isUnfilteredAndSearchedForBy:)
5751 with:nil
5752 ];
5753
5754 [table_ setShouldHideHeaderInShortLists:NO];
5755 [transition_ transition:0 toView:table_];
5756
5757 CGRect cnfrect = {{
5758 #ifdef __OBJC2__
5759 6 +
5760 #endif
5761 1, 38}, {17, 18}};
5762
5763 CGRect area;
5764 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5765 area.origin.y = 1;
5766
5767 area.size.width =
5768 #ifdef __OBJC2__
5769 8 +
5770 #endif
5771 [self bounds].size.width - area.origin.x - 18;
5772
5773 area.size.height = [UISearchField defaultHeight];
5774
5775 field_ = [[UISearchField alloc] initWithFrame:area];
5776
5777 UIFont *font = [UIFont systemFontOfSize:16];
5778 [field_ setFont:font];
5779
5780 [field_ setPlaceholder:@"Package Names & Descriptions"];
5781 [field_ setDelegate:self];
5782
5783 [field_ setPaddingTop:5];
5784
5785 UITextInputTraits *traits([field_ textInputTraits]);
5786 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5787 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5788 [traits setReturnKeyType:UIReturnKeySearch];
5789
5790 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
5791
5792 accessory_ = [[UIView alloc] initWithFrame:accrect];
5793 [accessory_ addSubview:field_];
5794
5795 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5796 [configure setShowPressFeedback:YES];
5797 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5798 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5799 [accessory_ addSubview:configure];*/
5800
5801 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5802 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5803 } return self;
5804 }
5805
5806 - (void) flipPage {
5807 #ifndef __OBJC2__
5808 LKAnimation *animation = [LKTransition animation];
5809 [animation setType:@"oglFlip"];
5810 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5811 [animation setFillMode:@"extended"];
5812 [animation setTransitionFlags:3];
5813 [animation setDuration:10];
5814 [animation setSpeed:0.35];
5815 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5816 [[transition_ _layer] addAnimation:animation forKey:0];
5817 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5818 flipped_ = !flipped_;
5819 #endif
5820 }
5821
5822 - (void) configurePushed {
5823 [field_ resignFirstResponder];
5824 [self flipPage];
5825 }
5826
5827 - (void) resetViewAnimated:(BOOL)animated {
5828 if (flipped_)
5829 [self flipPage];
5830 [table_ resetViewAnimated:animated];
5831 }
5832
5833 - (void) _reloadData {
5834 }
5835
5836 - (void) reloadData {
5837 if (flipped_)
5838 [self flipPage];
5839 [table_ setObject:[field_ text]];
5840 [table_ reloadData];
5841 [table_ resetCursor];
5842 }
5843
5844 - (UIView *) accessoryView {
5845 return accessory_;
5846 }
5847
5848 - (NSString *) title {
5849 return nil;
5850 }
5851
5852 - (NSString *) backButtonTitle {
5853 return @"Search";
5854 }
5855
5856 - (void) setDelegate:(id)delegate {
5857 [table_ setDelegate:delegate];
5858 [super setDelegate:delegate];
5859 }
5860
5861 @end
5862 /* }}} */
5863
5864 @interface SettingsView : RVPage {
5865 _transient Database *database_;
5866 NSString *name_;
5867 Package *package_;
5868 UIPreferencesTable *table_;
5869 _UISwitchSlider *subscribedSwitch_;
5870 _UISwitchSlider *ignoredSwitch_;
5871 UIPreferencesControlTableCell *subscribedCell_;
5872 UIPreferencesControlTableCell *ignoredCell_;
5873 }
5874
5875 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
5876
5877 @end
5878
5879 @implementation SettingsView
5880
5881 - (void) dealloc {
5882 [table_ setDataSource:nil];
5883
5884 [name_ release];
5885 if (package_ != nil)
5886 [package_ release];
5887 [table_ release];
5888 [subscribedSwitch_ release];
5889 [ignoredSwitch_ release];
5890 [subscribedCell_ release];
5891 [ignoredCell_ release];
5892 [super dealloc];
5893 }
5894
5895 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5896 if (package_ == nil)
5897 return 0;
5898
5899 return 2;
5900 }
5901
5902 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5903 if (package_ == nil)
5904 return nil;
5905
5906 switch (group) {
5907 case 0: return nil;
5908 case 1: return nil;
5909
5910 default: _assert(false);
5911 }
5912
5913 return nil;
5914 }
5915
5916 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
5917 if (package_ == nil)
5918 return NO;
5919
5920 switch (group) {
5921 case 0: return NO;
5922 case 1: return YES;
5923
5924 default: _assert(false);
5925 }
5926
5927 return NO;
5928 }
5929
5930 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5931 if (package_ == nil)
5932 return 0;
5933
5934 switch (group) {
5935 case 0: return 1;
5936 case 1: return 1;
5937
5938 default: _assert(false);
5939 }
5940
5941 return 0;
5942 }
5943
5944 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
5945 if (package_ == nil)
5946 return;
5947
5948 _UISwitchSlider *slider([cell control]);
5949 BOOL value([slider value] != 0);
5950 NSMutableDictionary *metadata([package_ metadata]);
5951
5952 BOOL before;
5953 if (NSNumber *number = [metadata objectForKey:key])
5954 before = [number boolValue];
5955 else
5956 before = NO;
5957
5958 if (value != before) {
5959 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
5960 Changed_ = true;
5961 [delegate_ updateData];
5962 }
5963 }
5964
5965 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
5966 [self onSomething:cell withKey:@"IsSubscribed"];
5967 }
5968
5969 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
5970 [self onSomething:cell withKey:@"IsIgnored"];
5971 }
5972
5973 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
5974 if (package_ == nil)
5975 return nil;
5976
5977 switch (group) {
5978 case 0: switch (row) {
5979 case 0:
5980 return subscribedCell_;
5981 case 1:
5982 return ignoredCell_;
5983 default: _assert(false);
5984 } break;
5985
5986 case 1: switch (row) {
5987 case 0: {
5988 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
5989 [cell setShowSelection:NO];
5990 [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."];
5991 return cell;
5992 }
5993
5994 default: _assert(false);
5995 } break;
5996
5997 default: _assert(false);
5998 }
5999
6000 return nil;
6001 }
6002
6003 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6004 if ((self = [super initWithBook:book])) {
6005 database_ = database;
6006 name_ = [package retain];
6007
6008 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6009 [self addSubview:table_];
6010
6011 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6012 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6013
6014 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6015 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6016
6017 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6018 [subscribedCell_ setShowSelection:NO];
6019 [subscribedCell_ setTitle:@"Show All Changes"];
6020 [subscribedCell_ setControl:subscribedSwitch_];
6021
6022 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6023 [ignoredCell_ setShowSelection:NO];
6024 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6025 [ignoredCell_ setControl:ignoredSwitch_];
6026
6027 [table_ setDataSource:self];
6028 [self reloadData];
6029 } return self;
6030 }
6031
6032 - (void) resetViewAnimated:(BOOL)animated {
6033 [table_ resetViewAnimated:animated];
6034 }
6035
6036 - (void) reloadData {
6037 if (package_ != nil)
6038 [package_ autorelease];
6039 package_ = [database_ packageWithName:name_];
6040 if (package_ != nil) {
6041 [package_ retain];
6042 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6043 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6044 }
6045
6046 [table_ reloadData];
6047 }
6048
6049 - (NSString *) title {
6050 return @"Settings";
6051 }
6052
6053 @end
6054
6055 /* Signature View {{{ */
6056 @interface SignatureView : BrowserView {
6057 _transient Database *database_;
6058 NSString *package_;
6059 }
6060
6061 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6062
6063 @end
6064
6065 @implementation SignatureView
6066
6067 - (void) dealloc {
6068 [package_ release];
6069 [super dealloc];
6070 }
6071
6072 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6073 // XXX: dude!
6074 [super webView:sender didClearWindowObject:window forFrame:frame];
6075 }
6076
6077 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6078 if ((self = [super initWithBook:book]) != nil) {
6079 database_ = database;
6080 package_ = [package retain];
6081 [self reloadData];
6082 } return self;
6083 }
6084
6085 - (void) resetViewAnimated:(BOOL)animated {
6086 }
6087
6088 - (void) reloadData {
6089 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6090 }
6091
6092 @end
6093 /* }}} */
6094
6095 @interface Cydia : UIApplication <
6096 ConfirmationViewDelegate,
6097 ProgressViewDelegate,
6098 SearchViewDelegate,
6099 CydiaDelegate
6100 > {
6101 UIWindow *window_;
6102
6103 UIView *underlay_;
6104 UIView *overlay_;
6105 CYBook *book_;
6106 UIToolbar *buttonbar_;
6107
6108 RVBook *confirm_;
6109
6110 NSMutableArray *essential_;
6111 NSMutableArray *broken_;
6112
6113 Database *database_;
6114 ProgressView *progress_;
6115
6116 unsigned tag_;
6117
6118 UIKeyboard *keyboard_;
6119 UIProgressHUD *hud_;
6120
6121 InstallView *install_;
6122 ChangesView *changes_;
6123 ManageView *manage_;
6124 SearchView *search_;
6125 }
6126
6127 @end
6128
6129 @implementation Cydia
6130
6131 - (void) _loaded {
6132 if ([broken_ count] != 0) {
6133 int count = [broken_ count];
6134
6135 UIActionSheet *sheet = [[[UIActionSheet alloc]
6136 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6137 buttons:[NSArray arrayWithObjects:
6138 @"Forcibly Clear",
6139 @"Ignore (Temporary)",
6140 nil]
6141 defaultButtonIndex:0
6142 delegate:self
6143 context:@"fixhalf"
6144 ] autorelease];
6145
6146 [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."];
6147 [sheet popupAlertAnimated:YES];
6148 } else if (!Ignored_ && [essential_ count] != 0) {
6149 int count = [essential_ count];
6150
6151 UIActionSheet *sheet = [[[UIActionSheet alloc]
6152 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6153 buttons:[NSArray arrayWithObjects:
6154 @"Upgrade Essential",
6155 @"Complete Upgrade",
6156 @"Ignore (Temporary)",
6157 nil]
6158 defaultButtonIndex:0
6159 delegate:self
6160 context:@"upgrade"
6161 ] autorelease];
6162
6163 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6164 [sheet popupAlertAnimated:YES];
6165 }
6166 }
6167
6168 - (void) _reloadData {
6169 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6170 [hud setText:@"Reloading Data"];
6171 [overlay_ addSubview:hud];
6172 [hud show:YES];*/
6173
6174 [database_ reloadData];
6175
6176 size_t changes(0);
6177
6178 [essential_ removeAllObjects];
6179 [broken_ removeAllObjects];
6180
6181 NSArray *packages = [database_ packages];
6182 for (Package *package in packages) {
6183 if ([package half])
6184 [broken_ addObject:package];
6185 if ([package upgradableAndEssential:NO]) {
6186 if ([package essential])
6187 [essential_ addObject:package];
6188 ++changes;
6189 }
6190 }
6191
6192 if (changes != 0) {
6193 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6194 [buttonbar_ setBadgeValue:badge forButton:3];
6195 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6196 [buttonbar_ setBadgeAnimated:YES forButton:3];
6197 [self setApplicationBadge:badge];
6198 } else {
6199 [buttonbar_ setBadgeValue:nil forButton:3];
6200 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6201 [buttonbar_ setBadgeAnimated:NO forButton:3];
6202 [self removeApplicationBadge];
6203 }
6204
6205 [self updateData];
6206
6207 #if !ForSaurik
6208 if ([packages count] == 0);
6209 else if (Loaded_)
6210 #endif
6211 [self _loaded];
6212 #if !ForSaurik
6213 else {
6214 Loaded_ = YES;
6215 [book_ update];
6216 }
6217 #endif
6218
6219 /*[hud show:NO];
6220 [hud removeFromSuperview];*/
6221 }
6222
6223 - (void) _saveConfig {
6224 if (Changed_) {
6225 _trace();
6226 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6227 _trace();
6228 Changed_ = false;
6229 }
6230 }
6231
6232 - (void) updateData {
6233 [self _saveConfig];
6234
6235 /* XXX: this is just stupid */
6236 if (tag_ != 2 && install_ != nil)
6237 [install_ reloadData];
6238 if (tag_ != 3 && changes_ != nil)
6239 [changes_ reloadData];
6240 if (tag_ != 5 && search_ != nil)
6241 [search_ reloadData];
6242
6243 [book_ reloadData];
6244 }
6245
6246 - (void) update_ {
6247 [database_ update];
6248 }
6249
6250 - (void) syncData {
6251 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6252 _assert(file != NULL);
6253
6254 NSArray *keys = [Sources_ allKeys];
6255
6256 for (int i(0), e([keys count]); i != e; ++i) {
6257 NSString *key = [keys objectAtIndex:i];
6258 NSDictionary *source = [Sources_ objectForKey:key];
6259
6260 fprintf(file, "%s %s %s\n",
6261 [[source objectForKey:@"Type"] UTF8String],
6262 [[source objectForKey:@"URI"] UTF8String],
6263 [[source objectForKey:@"Distribution"] UTF8String]
6264 );
6265 }
6266
6267 fclose(file);
6268
6269 [self _saveConfig];
6270
6271 [progress_
6272 detachNewThreadSelector:@selector(update_)
6273 toTarget:self
6274 withObject:nil
6275 title:@"Updating Sources"
6276 ];
6277 }
6278
6279 - (void) reloadData {
6280 @synchronized (self) {
6281 if (confirm_ == nil)
6282 [self _reloadData];
6283 }
6284 }
6285
6286 - (void) resolve {
6287 pkgProblemResolver *resolver = [database_ resolver];
6288
6289 resolver->InstallProtect();
6290 if (!resolver->Resolve(true))
6291 _error->Discard();
6292 }
6293
6294 - (void) perform {
6295 [database_ prepare];
6296
6297 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6298 [confirm_ setDelegate:self];
6299
6300 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6301 [page setDelegate:self];
6302
6303 [confirm_ setPage:page];
6304 [underlay_ popSubview:confirm_];
6305 }
6306
6307 - (void) installPackage:(Package *)package {
6308 @synchronized (self) {
6309 [package install];
6310 [self resolve];
6311 [self perform];
6312 }
6313 }
6314
6315 - (void) removePackage:(Package *)package {
6316 @synchronized (self) {
6317 [package remove];
6318 [self resolve];
6319 [self perform];
6320 }
6321 }
6322
6323 - (void) distUpgrade {
6324 @synchronized (self) {
6325 [database_ upgrade];
6326 [self perform];
6327 }
6328 }
6329
6330 - (void) cancel {
6331 @synchronized (self) {
6332 [self _reloadData];
6333 if (confirm_ != nil) {
6334 [confirm_ release];
6335 confirm_ = nil;
6336 }
6337 }
6338 }
6339
6340 - (void) confirm {
6341 [overlay_ removeFromSuperview];
6342 reload_ = true;
6343
6344 [progress_
6345 detachNewThreadSelector:@selector(perform)
6346 toTarget:database_
6347 withObject:nil
6348 title:@"Running"
6349 ];
6350 }
6351
6352 - (void) bootstrap_ {
6353 [database_ update];
6354 [database_ upgrade];
6355 [database_ prepare];
6356 [database_ perform];
6357 }
6358
6359 - (void) bootstrap {
6360 [progress_
6361 detachNewThreadSelector:@selector(bootstrap_)
6362 toTarget:self
6363 withObject:nil
6364 title:@"Bootstrap Install"
6365 ];
6366 }
6367
6368 - (void) progressViewIsComplete:(ProgressView *)progress {
6369 if (confirm_ != nil) {
6370 [underlay_ addSubview:overlay_];
6371 [confirm_ popFromSuperviewAnimated:NO];
6372 }
6373
6374 [self cancel];
6375 }
6376
6377 - (void) setPage:(RVPage *)page {
6378 [page resetViewAnimated:NO];
6379 [page setDelegate:self];
6380 [book_ setPage:page];
6381 }
6382
6383 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6384 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6385 [browser loadURL:url];
6386 return browser;
6387 }
6388
6389 - (void) _setHomePage {
6390 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6391 }
6392
6393 - (void) buttonBarItemTapped:(id)sender {
6394 unsigned tag = [sender tag];
6395 if (tag == tag_) {
6396 [book_ resetViewAnimated:YES];
6397 return;
6398 } else if (tag_ == 2 && tag != 2)
6399 [install_ resetView];
6400
6401 switch (tag) {
6402 case 1: [self _setHomePage]; break;
6403
6404 case 2: [self setPage:install_]; break;
6405 case 3: [self setPage:changes_]; break;
6406 case 4: [self setPage:manage_]; break;
6407 case 5: [self setPage:search_]; break;
6408
6409 default: _assert(false);
6410 }
6411
6412 tag_ = tag;
6413 }
6414
6415 - (void) applicationWillSuspend {
6416 [database_ clean];
6417 [super applicationWillSuspend];
6418 }
6419
6420 - (void) askForSettings {
6421 UIActionSheet *role = [[[UIActionSheet alloc]
6422 initWithTitle:@"Who Are You?"
6423 buttons:[NSArray arrayWithObjects:
6424 @"User (Graphical Only)",
6425 @"Hacker (+ Command Line)",
6426 @"Developer (No Filters)",
6427 nil]
6428 defaultButtonIndex:-1
6429 delegate:self
6430 context:@"role"
6431 ] autorelease];
6432
6433 [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."];
6434 [role popupAlertAnimated:YES];
6435 }
6436
6437 - (void) finish {
6438 if (hud_ != nil) {
6439 [self setStatusBarShowsProgress:NO];
6440
6441 [hud_ show:NO];
6442 [hud_ removeFromSuperview];
6443 [hud_ autorelease];
6444 hud_ = nil;
6445
6446 pid_t pid = ExecFork();
6447 if (pid == 0) {
6448 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6449 perror("launchctl stop");
6450 }
6451
6452 return;
6453 }
6454
6455 if (Role_ == nil) {
6456 [self askForSettings];
6457 return;
6458 }
6459
6460 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6461
6462 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6463 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6464 0, 0, screenrect.size.width, screenrect.size.height - 48
6465 ) database:database_];
6466
6467 [book_ setDelegate:self];
6468
6469 [overlay_ addSubview:book_];
6470
6471 NSArray *buttonitems = [NSArray arrayWithObjects:
6472 [NSDictionary dictionaryWithObjectsAndKeys:
6473 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6474 @"home-up.png", kUIButtonBarButtonInfo,
6475 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6476 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6477 self, kUIButtonBarButtonTarget,
6478 @"Home", kUIButtonBarButtonTitle,
6479 @"0", kUIButtonBarButtonType,
6480 nil],
6481
6482 [NSDictionary dictionaryWithObjectsAndKeys:
6483 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6484 @"install-up.png", kUIButtonBarButtonInfo,
6485 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6486 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6487 self, kUIButtonBarButtonTarget,
6488 @"Sections", kUIButtonBarButtonTitle,
6489 @"0", kUIButtonBarButtonType,
6490 nil],
6491
6492 [NSDictionary dictionaryWithObjectsAndKeys:
6493 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6494 @"changes-up.png", kUIButtonBarButtonInfo,
6495 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6496 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6497 self, kUIButtonBarButtonTarget,
6498 @"Changes", kUIButtonBarButtonTitle,
6499 @"0", kUIButtonBarButtonType,
6500 nil],
6501
6502 [NSDictionary dictionaryWithObjectsAndKeys:
6503 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6504 @"manage-up.png", kUIButtonBarButtonInfo,
6505 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6506 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6507 self, kUIButtonBarButtonTarget,
6508 @"Manage", kUIButtonBarButtonTitle,
6509 @"0", kUIButtonBarButtonType,
6510 nil],
6511
6512 [NSDictionary dictionaryWithObjectsAndKeys:
6513 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6514 @"search-up.png", kUIButtonBarButtonInfo,
6515 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6516 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6517 self, kUIButtonBarButtonTarget,
6518 @"Search", kUIButtonBarButtonTitle,
6519 @"0", kUIButtonBarButtonType,
6520 nil],
6521 nil];
6522
6523 buttonbar_ = [[UIToolbar alloc]
6524 initInView:overlay_
6525 withFrame:CGRectMake(
6526 0, screenrect.size.height - ButtonBarHeight_,
6527 screenrect.size.width, ButtonBarHeight_
6528 )
6529 withItemList:buttonitems
6530 ];
6531
6532 [buttonbar_ setDelegate:self];
6533 [buttonbar_ setBarStyle:1];
6534 [buttonbar_ setButtonBarTrackingMode:2];
6535
6536 int buttons[5] = {1, 2, 3, 4, 5};
6537 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6538 [buttonbar_ showButtonGroup:0 withDuration:0];
6539
6540 for (int i = 0; i != 5; ++i)
6541 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6542 i * 64 + 2, 1, 60, ButtonBarHeight_
6543 )];
6544
6545 [buttonbar_ showSelectionForButton:1];
6546 [overlay_ addSubview:buttonbar_];
6547
6548 [UIKeyboard initImplementationNow];
6549 CGSize keysize = [UIKeyboard defaultSize];
6550 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6551 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6552 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6553 [overlay_ addSubview:keyboard_];
6554
6555 if (!bootstrap_)
6556 [underlay_ addSubview:overlay_];
6557
6558 [self reloadData];
6559
6560 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6561 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6562 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6563
6564 manage_ = (ManageView *) [[self
6565 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6566 withClass:[ManageView class]
6567 ] retain];
6568
6569 if (bootstrap_)
6570 [self bootstrap];
6571 else
6572 [self _setHomePage];
6573 }
6574
6575 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6576 NSString *context([sheet context]);
6577
6578 if ([context isEqualToString:@"fixhalf"]) {
6579 switch (button) {
6580 case 1:
6581 @synchronized (self) {
6582 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6583 Package *broken = [broken_ objectAtIndex:i];
6584 [broken remove];
6585
6586 NSString *id = [broken id];
6587 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6588 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6589 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6590 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6591 }
6592
6593 [self resolve];
6594 [self perform];
6595 }
6596 break;
6597
6598 case 2:
6599 [broken_ removeAllObjects];
6600 [self _loaded];
6601 break;
6602
6603 default:
6604 _assert(false);
6605 }
6606
6607 [sheet dismiss];
6608 } else if ([context isEqualToString:@"role"]) {
6609 switch (button) {
6610 case 1: Role_ = @"User"; break;
6611 case 2: Role_ = @"Hacker"; break;
6612 case 3: Role_ = @"Developer"; break;
6613
6614 default:
6615 Role_ = nil;
6616 _assert(false);
6617 }
6618
6619 bool reset = Settings_ != nil;
6620
6621 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6622 Role_, @"Role",
6623 nil];
6624
6625 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6626
6627 Changed_ = true;
6628
6629 if (reset)
6630 [self updateData];
6631 else
6632 [self finish];
6633
6634 [sheet dismiss];
6635 } else if ([context isEqualToString:@"upgrade"]) {
6636 switch (button) {
6637 case 1:
6638 @synchronized (self) {
6639 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6640 Package *essential = [essential_ objectAtIndex:i];
6641 [essential install];
6642 }
6643
6644 [self resolve];
6645 [self perform];
6646 }
6647 break;
6648
6649 case 2:
6650 [self distUpgrade];
6651 break;
6652
6653 case 3:
6654 Ignored_ = YES;
6655 break;
6656
6657 default:
6658 _assert(false);
6659 }
6660
6661 [sheet dismiss];
6662 }
6663 }
6664
6665 - (void) reorganize { _pooled
6666 system("/usr/libexec/cydia/free.sh");
6667 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6668 }
6669
6670 - (void) applicationSuspend:(__GSEvent *)event {
6671 if (hud_ == nil && ![progress_ isRunning])
6672 [super applicationSuspend:event];
6673 }
6674
6675 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6676 if (hud_ == nil)
6677 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6678 }
6679
6680 - (void) _setSuspended:(BOOL)value {
6681 if (hud_ == nil)
6682 [super _setSuspended:value];
6683 }
6684
6685 - (UIProgressHUD *) addProgressHUD {
6686 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6687 [hud show:YES];
6688 [underlay_ addSubview:hud];
6689 return hud;
6690 }
6691
6692 - (void) openMailToURL:(NSURL *)url {
6693 // XXX: this makes me sad
6694 #if 0
6695 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
6696 #else
6697 [UIApp openURL:url];// asPanel:YES];
6698 #endif
6699 }
6700
6701 - (void) clearFirstResponder {
6702 if (id responder = [window_ firstResponder])
6703 [responder resignFirstResponder];
6704 }
6705
6706 - (RVPage *) pageForPackage:(NSString *)name {
6707 if (Package *package = [database_ packageWithName:name]) {
6708 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6709 [view setPackage:package];
6710 return view;
6711 } else {
6712 UIActionSheet *sheet = [[[UIActionSheet alloc]
6713 initWithTitle:@"Cannot Locate Package"
6714 buttons:[NSArray arrayWithObjects:@"Close", nil]
6715 defaultButtonIndex:0
6716 delegate:self
6717 context:@"missing"
6718 ] autorelease];
6719
6720 [sheet setBodyText:[NSString stringWithFormat:
6721 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
6722 , name]];
6723
6724 [sheet popupAlertAnimated:YES];
6725 return nil;
6726 }
6727 }
6728
6729 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
6730 NSString *href = [url absoluteString];
6731
6732 if (tag != NULL)
6733 tag = 0;
6734
6735 if ([href isEqualToString:@"cydia://add-source"])
6736 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
6737 else if ([href isEqualToString:@"cydia://storage"])
6738 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
6739 else if ([href isEqualToString:@"cydia://sources"])
6740 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
6741 else if ([href isEqualToString:@"cydia://packages"])
6742 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
6743 else if ([href hasPrefix:@"cydia://url/"])
6744 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
6745 else if ([href hasPrefix:@"cydia://launch/"])
6746 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
6747 else if ([href hasPrefix:@"cydia://package-settings/"])
6748 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
6749 else if ([href hasPrefix:@"cydia://package-signature/"])
6750 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
6751 else if ([href hasPrefix:@"cydia://package/"])
6752 return [self pageForPackage:[href substringFromIndex:16]];
6753 else if ([href hasPrefix:@"cydia://files/"]) {
6754 NSString *name = [href substringFromIndex:14];
6755
6756 if (Package *package = [database_ packageWithName:name]) {
6757 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
6758 [files setPackage:package];
6759 return files;
6760 }
6761 }
6762
6763 return nil;
6764 }
6765
6766 - (void) applicationOpenURL:(NSURL *)url {
6767 [super applicationOpenURL:url];
6768 int tag;
6769 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
6770 [self setPage:page];
6771 [buttonbar_ showSelectionForButton:tag];
6772 tag_ = tag;
6773 }
6774 }
6775
6776 - (void) applicationDidFinishLaunching:(id)unused {
6777 Font12_ = [[UIFont systemFontOfSize:12] retain];
6778 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6779 Font14_ = [[UIFont systemFontOfSize:14] retain];
6780 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6781 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6782
6783 _assert(pkgInitConfig(*_config));
6784 _assert(pkgInitSystem(*_config, _system));
6785
6786 tag_ = 1;
6787
6788 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6789 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6790
6791 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
6792
6793 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6794 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6795
6796 [window_ orderFront:self];
6797 [window_ makeKey:self];
6798 [window_ setHidden:NO];
6799
6800 database_ = [Database sharedInstance];
6801 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6802 [database_ setDelegate:progress_];
6803 [window_ setContentView:progress_];
6804
6805 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6806 [progress_ setContentView:underlay_];
6807
6808 [progress_ resetView];
6809
6810 if (
6811 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6812 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6813 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6814 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6815 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6816 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
6817 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
6818 ) {
6819 [self setIdleTimerDisabled:YES];
6820
6821 hud_ = [self addProgressHUD];
6822 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
6823
6824 [self setStatusBarShowsProgress:YES];
6825
6826 [NSThread
6827 detachNewThreadSelector:@selector(reorganize)
6828 toTarget:self
6829 withObject:nil
6830 ];
6831 } else
6832 [self finish];
6833 }
6834
6835 - (void) showKeyboard:(BOOL)show {
6836 CGSize keysize = [UIKeyboard defaultSize];
6837 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6838 CGRect keyup = keydown;
6839 keyup.origin.y -= keysize.height;
6840
6841 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6842 [animation setSignificantRectFields:2];
6843
6844 if (show) {
6845 [animation setStartFrame:keydown];
6846 [animation setEndFrame:keyup];
6847 [keyboard_ activate];
6848 } else {
6849 [animation setStartFrame:keyup];
6850 [animation setEndFrame:keydown];
6851 [keyboard_ deactivate];
6852 }
6853
6854 [[UIAnimator sharedAnimator]
6855 addAnimations:[NSArray arrayWithObjects:animation, nil]
6856 withDuration:KeyboardTime_
6857 start:YES
6858 ];
6859 }
6860
6861 - (void) slideUp:(UIActionSheet *)alert {
6862 if (Advanced_)
6863 [alert presentSheetFromButtonBar:buttonbar_];
6864 else
6865 [alert presentSheetInView:overlay_];
6866 }
6867
6868 @end
6869
6870 void AddPreferences(NSString *plist) { _pooled
6871 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6872 _assert(settings != NULL);
6873 NSMutableArray *items = [settings objectForKey:@"items"];
6874
6875 bool cydia(false);
6876
6877 for (size_t i(0); i != [items count]; ++i) {
6878 NSMutableDictionary *item([items objectAtIndex:i]);
6879 NSString *label = [item objectForKey:@"label"];
6880 if (label != nil && [label isEqualToString:@"Cydia"]) {
6881 cydia = true;
6882 break;
6883 }
6884 }
6885
6886 if (!cydia) {
6887 for (size_t i(0); i != [items count]; ++i) {
6888 NSDictionary *item([items objectAtIndex:i]);
6889 NSString *label = [item objectForKey:@"label"];
6890 if (label != nil && [label isEqualToString:@"General"]) {
6891 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6892 @"CydiaSettings", @"bundle",
6893 @"PSLinkCell", @"cell",
6894 [NSNumber numberWithBool:YES], @"hasIcon",
6895 [NSNumber numberWithBool:YES], @"isController",
6896 @"Cydia", @"label",
6897 nil] atIndex:(i + 1)];
6898
6899 break;
6900 }
6901 }
6902
6903 _assert([settings writeToFile:plist atomically:YES] == YES);
6904 }
6905 }
6906
6907 /*IMP alloc_;
6908 id Alloc_(id self, SEL selector) {
6909 id object = alloc_(self, selector);
6910 lprintf("[%s]A-%p\n", self->isa->name, object);
6911 return object;
6912 }*/
6913
6914 /*IMP dealloc_;
6915 id Dealloc_(id self, SEL selector) {
6916 id object = dealloc_(self, selector);
6917 lprintf("[%s]D-%p\n", self->isa->name, object);
6918 return object;
6919 }*/
6920
6921 int main(int argc, char *argv[]) { _pooled
6922 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
6923
6924 bool substrate(false);
6925
6926 if (argc != 0) {
6927 char **args(argv);
6928 int arge(1);
6929
6930 for (int argi(1); argi != argc; ++argi)
6931 if (strcmp(argv[argi], "--") == 0) {
6932 arge = argi;
6933 argv[argi] = argv[0];
6934 argv += argi;
6935 argc -= argi;
6936 break;
6937 }
6938
6939 for (int argi(1); argi != arge; ++argi)
6940 if (strcmp(args[argi], "--bootstrap") == 0)
6941 bootstrap_ = true;
6942 else if (strcmp(args[argi], "--substrate") == 0)
6943 substrate = true;
6944 else
6945 fprintf(stderr, "unknown argument: %s\n", args[argi]);
6946 }
6947
6948 App_ = [[NSBundle mainBundle] bundlePath];
6949 Home_ = NSHomeDirectory();
6950 Locale_ = CFLocaleCopyCurrent();
6951
6952 {
6953 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6954 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6955 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6956 Sounds_Keyboard_ = [keyboard boolValue];
6957 }
6958
6959 setuid(0);
6960 setgid(0);
6961
6962 #if 1 /* XXX: this costs 1.4s of startup performance */
6963 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6964 _assert(errno == ENOENT);
6965 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6966 _assert(errno == ENOENT);
6967 #endif
6968
6969 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6970 alloc_ = alloc->method_imp;
6971 alloc->method_imp = (IMP) &Alloc_;*/
6972
6973 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6974 dealloc_ = dealloc->method_imp;
6975 dealloc->method_imp = (IMP) &Dealloc_;*/
6976
6977 size_t size;
6978
6979 int maxproc;
6980 size = sizeof(maxproc);
6981 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
6982 perror("sysctlbyname(\"kern.maxproc\", ?)");
6983 else if (maxproc < 64) {
6984 maxproc = 64;
6985 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
6986 perror("sysctlbyname(\"kern.maxproc\", #)");
6987 }
6988
6989 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6990 char *machine = new char[size];
6991 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
6992 perror("sysctlbyname(\"hw.machine\", ?)");
6993 else
6994 Machine_ = machine;
6995
6996 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
6997
6998 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6999 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7000
7001 /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7002 Indices_ = [[NSMutableDictionary alloc] init];*/
7003
7004 Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7005 //@"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
7006 @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
7007 nil];
7008
7009 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7010 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7011 else {
7012 Settings_ = [Metadata_ objectForKey:@"Settings"];
7013
7014 Packages_ = [Metadata_ objectForKey:@"Packages"];
7015 Sections_ = [Metadata_ objectForKey:@"Sections"];
7016 Sources_ = [Metadata_ objectForKey:@"Sources"];
7017 }
7018
7019 if (Settings_ != nil)
7020 Role_ = [Settings_ objectForKey:@"Role"];
7021
7022 if (Packages_ == nil) {
7023 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7024 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7025 }
7026
7027 if (Sections_ == nil) {
7028 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7029 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7030 }
7031
7032 if (Sources_ == nil) {
7033 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7034 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7035 }
7036
7037 #if RecycleWebViews
7038 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7039 #endif
7040
7041 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7042 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7043
7044 if (access("/User", F_OK) != 0)
7045 system("/usr/libexec/cydia/firmware.sh");
7046
7047 _assert([[NSFileManager defaultManager]
7048 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7049 withIntermediateDirectories:YES
7050 attributes:nil
7051 error:NULL
7052 ]);
7053
7054 space_ = CGColorSpaceCreateDeviceRGB();
7055
7056 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7057 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7058 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7059 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7060 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7061 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7062
7063 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7064
7065 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7066
7067 UIApplicationUseLegacyEvents(YES);
7068 UIKeyboardDisableAutomaticAppearance();
7069
7070 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7071
7072 CGColorSpaceRelease(space_);
7073 CFRelease(Locale_);
7074
7075 return value;
7076 }