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