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