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