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