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