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