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