]> git.saurik.com Git - cydia.git/blob - Cydia.mm
My god his is taking me forever.
[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 int count = [buttons_ count];
4237 _assert(count != 0);
4238
4239 if (count == 1)
4240 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
4241 else {
4242 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
4243 [buttons addObjectsFromArray:buttons_];
4244 [buttons addObject:@"Cancel"];
4245
4246 [delegate_ slideUp:[[[UIActionSheet alloc]
4247 initWithTitle:nil
4248 buttons:buttons
4249 defaultButtonIndex:2
4250 delegate:self
4251 context:@"modify"
4252 ] autorelease]];
4253 }
4254 }
4255
4256 - (void) _rightButtonClicked {
4257 if (commercial_)
4258 [super _rightButtonClicked];
4259 else
4260 [self __rightButtonClicked];
4261 }
4262 #endif
4263
4264 - (id) _rightButtonTitle {
4265 int count = [buttons_ count];
4266 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
4267 }
4268
4269 - (NSString *) backButtonTitle {
4270 return @"Details";
4271 }
4272
4273 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4274 if ((self = [super initWithBook:book]) != nil) {
4275 database_ = database;
4276 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
4277 } return self;
4278 }
4279
4280 - (void) setPackage:(Package *)package {
4281 if (package_ != nil) {
4282 [package_ autorelease];
4283 package_ = nil;
4284 }
4285
4286 if (name_ != nil) {
4287 [name_ release];
4288 name_ = nil;
4289 }
4290
4291 [buttons_ removeAllObjects];
4292
4293 if (package != nil) {
4294 package_ = [package retain];
4295 name_ = [[package id] retain];
4296 commercial_ = [package isCommercial];
4297
4298 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
4299
4300 if ([package_ source] == nil);
4301 else if ([package_ upgradableAndEssential:NO])
4302 [buttons_ addObject:@"Upgrade"];
4303 else if ([package_ installed] == nil)
4304 [buttons_ addObject:@"Install"];
4305 else
4306 [buttons_ addObject:@"Reinstall"];
4307 if ([package_ installed] != nil)
4308 [buttons_ addObject:@"Remove"];
4309 }
4310 }
4311
4312 - (bool) isLoading {
4313 return commercial_ ? [super isLoading] : false;
4314 }
4315
4316 - (void) reloadData {
4317 [self setPackage:[database_ packageWithName:name_]];
4318 [self reloadButtons];
4319 }
4320
4321 @end
4322 /* }}} */
4323 /* Package Table {{{ */
4324 @interface PackageTable : RVPage {
4325 _transient Database *database_;
4326 NSString *title_;
4327 NSMutableArray *packages_;
4328 NSMutableArray *sections_;
4329 UISectionList *list_;
4330 }
4331
4332 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
4333
4334 - (void) setDelegate:(id)delegate;
4335
4336 - (void) reloadData;
4337 - (void) resetCursor;
4338
4339 - (UISectionList *) list;
4340
4341 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4342
4343 @end
4344
4345 @implementation PackageTable
4346
4347 - (void) dealloc {
4348 [list_ setDataSource:nil];
4349
4350 [title_ release];
4351 [packages_ release];
4352 [sections_ release];
4353 [list_ release];
4354 [super dealloc];
4355 }
4356
4357 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4358 return [sections_ count];
4359 }
4360
4361 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4362 return [[sections_ objectAtIndex:section] name];
4363 }
4364
4365 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4366 return [[sections_ objectAtIndex:section] row];
4367 }
4368
4369 - (int) numberOfRowsInTable:(UITable *)table {
4370 return [packages_ count];
4371 }
4372
4373 - (float) table:(UITable *)table heightForRow:(int)row {
4374 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4375 }
4376
4377 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4378 if (reusing == nil)
4379 reusing = [[[PackageCell alloc] init] autorelease];
4380 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4381 return reusing;
4382 }
4383
4384 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4385 return NO;
4386 }
4387
4388 - (void) tableRowSelected:(NSNotification *)notification {
4389 int row = [[notification object] selectedRow];
4390 if (row == INT_MAX)
4391 return;
4392
4393 Package *package = [packages_ objectAtIndex:row];
4394 package = [database_ packageWithName:[package id]];
4395 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4396 [view setPackage:package];
4397 [view setDelegate:delegate_];
4398 [book_ pushPage:view];
4399 }
4400
4401 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
4402 if ((self = [super initWithBook:book]) != nil) {
4403 database_ = database;
4404 title_ = [title retain];
4405
4406 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4407 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4408
4409 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4410 [list_ setDataSource:self];
4411
4412 UITableColumn *column = [[[UITableColumn alloc]
4413 initWithTitle:@"Name"
4414 identifier:@"name"
4415 width:[self frame].size.width
4416 ] autorelease];
4417
4418 UITable *table = [list_ table];
4419 [table setSeparatorStyle:1];
4420 [table addTableColumn:column];
4421 [table setDelegate:self];
4422 [table setReusesTableCells:YES];
4423
4424 [self addSubview:list_];
4425
4426 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4427 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4428 } return self;
4429 }
4430
4431 - (void) setDelegate:(id)delegate {
4432 delegate_ = delegate;
4433 }
4434
4435 - (bool) hasPackage:(Package *)package {
4436 return true;
4437 }
4438
4439 - (void) reloadData {
4440 NSArray *packages = [database_ packages];
4441
4442 [packages_ removeAllObjects];
4443 [sections_ removeAllObjects];
4444
4445 _profile(PackageTable$reloadData$Filter)
4446 for (Package *package in packages)
4447 if ([self hasPackage:package])
4448 [packages_ addObject:package];
4449 _end
4450
4451 Section *section = nil;
4452
4453 _profile(PackageTable$reloadData$Section)
4454 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
4455 Package *package;
4456 unichar index;
4457
4458 _profile(PackageTable$reloadData$Section$Package)
4459 package = [packages_ objectAtIndex:offset];
4460 index = [package index];
4461 _end
4462
4463 if (section == nil || [section index] != index) {
4464 _profile(PackageTable$reloadData$Section$Allocate)
4465 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
4466 _end
4467
4468 _profile(PackageTable$reloadData$Section$Add)
4469 [sections_ addObject:section];
4470 _end
4471 }
4472
4473 [section addToCount];
4474 }
4475 _end
4476
4477 _profile(PackageTable$reloadData$List)
4478 [list_ reloadData];
4479 _end
4480 }
4481
4482 - (NSString *) title {
4483 return title_;
4484 }
4485
4486 - (void) resetViewAnimated:(BOOL)animated {
4487 [list_ resetViewAnimated:animated];
4488 }
4489
4490 - (void) resetCursor {
4491 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4492 }
4493
4494 - (UISectionList *) list {
4495 return list_;
4496 }
4497
4498 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4499 [list_ setShouldHideHeaderInShortLists:hide];
4500 }
4501
4502 @end
4503 /* }}} */
4504 /* Filtered Package Table {{{ */
4505 @interface FilteredPackageTable : PackageTable {
4506 SEL filter_;
4507 IMP imp_;
4508 id object_;
4509 }
4510
4511 - (void) setObject:(id)object;
4512
4513 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
4514
4515 @end
4516
4517 @implementation FilteredPackageTable
4518
4519 - (void) dealloc {
4520 if (object_ != nil)
4521 [object_ release];
4522 [super dealloc];
4523 }
4524
4525 - (void) setObject:(id)object {
4526 if (object_ != nil)
4527 [object_ release];
4528 if (object == nil)
4529 object_ = nil;
4530 else
4531 object_ = [object retain];
4532 }
4533
4534 - (bool) hasPackage:(Package *)package {
4535 _profile(FilteredPackageTable$hasPackage)
4536 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
4537 _end
4538 }
4539
4540 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4541 if ((self = [super initWithBook:book database:database title:title]) != nil) {
4542 filter_ = filter;
4543 object_ = object == nil ? nil : [object retain];
4544
4545 /* XXX: this is an unsafe optimization of doomy hell */
4546 Method method = class_getInstanceMethod([Package class], filter);
4547 imp_ = method_getImplementation(method);
4548 _assert(imp_ != NULL);
4549
4550 [self reloadData];
4551 } return self;
4552 }
4553
4554 @end
4555 /* }}} */
4556
4557 /* Add Source View {{{ */
4558 @interface AddSourceView : RVPage {
4559 _transient Database *database_;
4560 }
4561
4562 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4563
4564 @end
4565
4566 @implementation AddSourceView
4567
4568 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4569 if ((self = [super initWithBook:book]) != nil) {
4570 database_ = database;
4571 } return self;
4572 }
4573
4574 @end
4575 /* }}} */
4576 /* Source Cell {{{ */
4577 @interface SourceCell : UITableCell {
4578 UIImage *icon_;
4579 NSString *origin_;
4580 NSString *description_;
4581 NSString *label_;
4582 }
4583
4584 - (void) dealloc;
4585
4586 - (SourceCell *) initWithSource:(Source *)source;
4587
4588 @end
4589
4590 @implementation SourceCell
4591
4592 - (void) dealloc {
4593 [icon_ release];
4594 [origin_ release];
4595 [description_ release];
4596 [label_ release];
4597 [super dealloc];
4598 }
4599
4600 - (SourceCell *) initWithSource:(Source *)source {
4601 if ((self = [super init]) != nil) {
4602 if (icon_ == nil)
4603 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4604 if (icon_ == nil)
4605 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4606 icon_ = [icon_ retain];
4607
4608 origin_ = [[source name] retain];
4609 label_ = [[source uri] retain];
4610 description_ = [[source description] retain];
4611 } return self;
4612 }
4613
4614 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4615 if (icon_ != nil)
4616 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
4617
4618 if (selected)
4619 UISetColor(White_);
4620
4621 if (!selected)
4622 UISetColor(Black_);
4623 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
4624
4625 if (!selected)
4626 UISetColor(Blue_);
4627 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
4628
4629 if (!selected)
4630 UISetColor(Gray_);
4631 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
4632
4633 [super drawContentInRect:rect selected:selected];
4634 }
4635
4636 @end
4637 /* }}} */
4638 /* Source Table {{{ */
4639 @interface SourceTable : RVPage {
4640 _transient Database *database_;
4641 UISectionList *list_;
4642 NSMutableArray *sources_;
4643 UIActionSheet *alert_;
4644 int offset_;
4645
4646 NSString *href_;
4647 UIProgressHUD *hud_;
4648 NSError *error_;
4649
4650 //NSURLConnection *installer_;
4651 NSURLConnection *trivial_bz2_;
4652 NSURLConnection *trivial_gz_;
4653 //NSURLConnection *automatic_;
4654
4655 BOOL trivial_;
4656 }
4657
4658 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4659
4660 @end
4661
4662 @implementation SourceTable
4663
4664 - (void) _deallocConnection:(NSURLConnection *)connection {
4665 if (connection != nil) {
4666 [connection cancel];
4667 //[connection setDelegate:nil];
4668 [connection release];
4669 }
4670 }
4671
4672 - (void) dealloc {
4673 [[list_ table] setDelegate:nil];
4674 [list_ setDataSource:nil];
4675
4676 if (href_ != nil)
4677 [href_ release];
4678 if (hud_ != nil)
4679 [hud_ release];
4680 if (error_ != nil)
4681 [error_ release];
4682
4683 //[self _deallocConnection:installer_];
4684 [self _deallocConnection:trivial_gz_];
4685 [self _deallocConnection:trivial_bz2_];
4686 //[self _deallocConnection:automatic_];
4687
4688 [sources_ release];
4689 [list_ release];
4690 [super dealloc];
4691 }
4692
4693 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4694 return offset_ == 0 ? 1 : 2;
4695 }
4696
4697 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4698 switch (section + (offset_ == 0 ? 1 : 0)) {
4699 case 0: return @"Entered by User";
4700 case 1: return @"Installed by Packages";
4701
4702 default:
4703 _assert(false);
4704 return nil;
4705 }
4706 }
4707
4708 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4709 switch (section + (offset_ == 0 ? 1 : 0)) {
4710 case 0: return 0;
4711 case 1: return offset_;
4712
4713 default:
4714 _assert(false);
4715 return -1;
4716 }
4717 }
4718
4719 - (int) numberOfRowsInTable:(UITable *)table {
4720 return [sources_ count];
4721 }
4722
4723 - (float) table:(UITable *)table heightForRow:(int)row {
4724 Source *source = [sources_ objectAtIndex:row];
4725 return [source description] == nil ? 56 : 73;
4726 }
4727
4728 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4729 Source *source = [sources_ objectAtIndex:row];
4730 // XXX: weird warning, stupid selectors ;P
4731 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4732 }
4733
4734 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4735 return YES;
4736 }
4737
4738 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4739 return YES;
4740 }
4741
4742 - (void) tableRowSelected:(NSNotification*)notification {
4743 UITable *table([list_ table]);
4744 int row([table selectedRow]);
4745 if (row == INT_MAX)
4746 return;
4747
4748 Source *source = [sources_ objectAtIndex:row];
4749
4750 PackageTable *packages = [[[FilteredPackageTable alloc]
4751 initWithBook:book_
4752 database:database_
4753 title:[source label]
4754 filter:@selector(isVisibleInSource:)
4755 with:source
4756 ] autorelease];
4757
4758 [packages setDelegate:delegate_];
4759
4760 [book_ pushPage:packages];
4761 }
4762
4763 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4764 Source *source = [sources_ objectAtIndex:row];
4765 return [source record] != nil;
4766 }
4767
4768 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4769 [[list_ table] setDeleteConfirmationRow:row];
4770 }
4771
4772 - (void) table:(UITable *)table deleteRow:(int)row {
4773 Source *source = [sources_ objectAtIndex:row];
4774 [Sources_ removeObjectForKey:[source key]];
4775 [delegate_ syncData];
4776 }
4777
4778 - (void) complete {
4779 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4780 @"deb", @"Type",
4781 href_, @"URI",
4782 @"./", @"Distribution",
4783 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4784
4785 [delegate_ syncData];
4786 }
4787
4788 - (NSString *) getWarning {
4789 NSString *href(href_);
4790 NSRange colon([href rangeOfString:@"://"]);
4791 if (colon.location != NSNotFound)
4792 href = [href substringFromIndex:(colon.location + 3)];
4793 href = [href stringByAddingPercentEscapes];
4794 href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
4795 href = [href stringByCachingURLWithCurrentCDN];
4796
4797 NSURL *url([NSURL URLWithString:href]);
4798
4799 NSStringEncoding encoding;
4800 NSError *error(nil);
4801
4802 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
4803 return [warning length] == 0 ? nil : warning;
4804 return nil;
4805 }
4806
4807 - (void) _endConnection:(NSURLConnection *)connection {
4808 NSURLConnection **field = NULL;
4809 if (connection == trivial_bz2_)
4810 field = &trivial_bz2_;
4811 else if (connection == trivial_gz_)
4812 field = &trivial_gz_;
4813 _assert(field != NULL);
4814 [connection release];
4815 *field = nil;
4816
4817 if (
4818 trivial_bz2_ == nil &&
4819 trivial_gz_ == nil
4820 ) {
4821 bool defer(false);
4822
4823 if (trivial_) {
4824 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
4825 defer = true;
4826
4827 UIActionSheet *sheet = [[[UIActionSheet alloc]
4828 initWithTitle:@"Source Warning"
4829 buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
4830 defaultButtonIndex:0
4831 delegate:self
4832 context:@"warning"
4833 ] autorelease];
4834
4835 [sheet setNumberOfRows:1];
4836
4837 [sheet setBodyText:warning];
4838 [sheet popupAlertAnimated:YES];
4839 } else
4840 [self complete];
4841 } else if (error_ != nil) {
4842 UIActionSheet *sheet = [[[UIActionSheet alloc]
4843 initWithTitle:@"Verification Error"
4844 buttons:[NSArray arrayWithObjects:@"OK", nil]
4845 defaultButtonIndex:0
4846 delegate:self
4847 context:@"urlerror"
4848 ] autorelease];
4849
4850 [sheet setBodyText:[error_ localizedDescription]];
4851 [sheet popupAlertAnimated:YES];
4852 } else {
4853 UIActionSheet *sheet = [[[UIActionSheet alloc]
4854 initWithTitle:@"Did not Find Repository"
4855 buttons:[NSArray arrayWithObjects:@"OK", nil]
4856 defaultButtonIndex:0
4857 delegate:self
4858 context:@"trivial"
4859 ] autorelease];
4860
4861 [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."];
4862 [sheet popupAlertAnimated:YES];
4863 }
4864
4865 [delegate_ setStatusBarShowsProgress:NO];
4866 [delegate_ removeProgressHUD:hud_];
4867
4868 [hud_ autorelease];
4869 hud_ = nil;
4870
4871 if (!defer) {
4872 [href_ release];
4873 href_ = nil;
4874 }
4875
4876 if (error_ != nil) {
4877 [error_ release];
4878 error_ = nil;
4879 }
4880 }
4881 }
4882
4883 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4884 switch ([response statusCode]) {
4885 case 200:
4886 trivial_ = YES;
4887 }
4888 }
4889
4890 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
4891 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
4892 if (error_ != nil)
4893 error_ = [error retain];
4894 [self _endConnection:connection];
4895 }
4896
4897 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4898 [self _endConnection:connection];
4899 }
4900
4901 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4902 NSMutableURLRequest *request = [NSMutableURLRequest
4903 requestWithURL:[NSURL URLWithString:href]
4904 cachePolicy:NSURLRequestUseProtocolCachePolicy
4905 timeoutInterval:20.0
4906 ];
4907
4908 [request setHTTPMethod:method];
4909
4910 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4911 }
4912
4913 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4914 NSString *context([sheet context]);
4915
4916 if ([context isEqualToString:@"source"]) {
4917 switch (button) {
4918 case 1: {
4919 NSString *href = [[sheet textField] text];
4920
4921 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4922
4923 if (![href hasSuffix:@"/"])
4924 href_ = [href stringByAppendingString:@"/"];
4925 else
4926 href_ = href;
4927 href_ = [href_ retain];
4928
4929 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4930 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4931 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4932
4933 trivial_ = false;
4934
4935 hud_ = [[delegate_ addProgressHUD] retain];
4936 [hud_ setText:@"Verifying URL"];
4937 } break;
4938
4939 case 2:
4940 break;
4941
4942 default:
4943 _assert(false);
4944 }
4945
4946 [sheet dismiss];
4947 } else if ([context isEqualToString:@"trivial"])
4948 [sheet dismiss];
4949 else if ([context isEqualToString:@"urlerror"])
4950 [sheet dismiss];
4951 else if ([context isEqualToString:@"warning"]) {
4952 switch (button) {
4953 case 1:
4954 [self complete];
4955 break;
4956
4957 case 2:
4958 break;
4959
4960 default:
4961 _assert(false);
4962 }
4963
4964 [href_ release];
4965 href_ = nil;
4966
4967 [sheet dismiss];
4968 }
4969 }
4970
4971 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4972 if ((self = [super initWithBook:book]) != nil) {
4973 database_ = database;
4974 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4975
4976 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4977 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4978 [list_ setShouldHideHeaderInShortLists:NO];
4979
4980 [self addSubview:list_];
4981 [list_ setDataSource:self];
4982
4983 UITableColumn *column = [[UITableColumn alloc]
4984 initWithTitle:@"Name"
4985 identifier:@"name"
4986 width:[self frame].size.width
4987 ];
4988
4989 UITable *table = [list_ table];
4990 [table setSeparatorStyle:1];
4991 [table addTableColumn:column];
4992 [table setDelegate:self];
4993
4994 [self reloadData];
4995
4996 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4997 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4998 } return self;
4999 }
5000
5001 - (void) reloadData {
5002 pkgSourceList list;
5003 _assert(list.ReadMainList());
5004
5005 [sources_ removeAllObjects];
5006 [sources_ addObjectsFromArray:[database_ sources]];
5007 _trace();
5008 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
5009 _trace();
5010
5011 int count = [sources_ count];
5012 for (offset_ = 0; offset_ != count; ++offset_) {
5013 Source *source = [sources_ objectAtIndex:offset_];
5014 if ([source record] == nil)
5015 break;
5016 }
5017
5018 [list_ reloadData];
5019 }
5020
5021 - (void) resetViewAnimated:(BOOL)animated {
5022 [list_ resetViewAnimated:animated];
5023 }
5024
5025 - (void) _leftButtonClicked {
5026 /*[book_ pushPage:[[[AddSourceView alloc]
5027 initWithBook:book_
5028 database:database_
5029 ] autorelease]];*/
5030
5031 UIActionSheet *sheet = [[[UIActionSheet alloc]
5032 initWithTitle:@"Enter Cydia/APT URL"
5033 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
5034 defaultButtonIndex:0
5035 delegate:self
5036 context:@"source"
5037 ] autorelease];
5038
5039 [sheet setNumberOfRows:1];
5040
5041 [sheet addTextFieldWithValue:@"http://" label:@""];
5042
5043 UITextInputTraits *traits = [[sheet textField] textInputTraits];
5044 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5045 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5046 [traits setKeyboardType:UIKeyboardTypeURL];
5047 // XXX: UIReturnKeyDone
5048 [traits setReturnKeyType:UIReturnKeyNext];
5049
5050 [sheet popupAlertAnimated:YES];
5051 }
5052
5053 - (void) _rightButtonClicked {
5054 UITable *table = [list_ table];
5055 BOOL editing = [table isRowDeletionEnabled];
5056 [table enableRowDeletion:!editing animated:YES];
5057 [book_ reloadButtonsForPage:self];
5058 }
5059
5060 - (NSString *) title {
5061 return @"Sources";
5062 }
5063
5064 - (NSString *) leftButtonTitle {
5065 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
5066 }
5067
5068 - (id) rightButtonTitle {
5069 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
5070 }
5071
5072 - (UINavigationButtonStyle) rightButtonStyle {
5073 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5074 }
5075
5076 @end
5077 /* }}} */
5078
5079 /* Installed View {{{ */
5080 @interface InstalledView : RVPage {
5081 _transient Database *database_;
5082 FilteredPackageTable *packages_;
5083 BOOL expert_;
5084 }
5085
5086 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5087
5088 @end
5089
5090 @implementation InstalledView
5091
5092 - (void) dealloc {
5093 [packages_ release];
5094 [super dealloc];
5095 }
5096
5097 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5098 if ((self = [super initWithBook:book]) != nil) {
5099 database_ = database;
5100
5101 packages_ = [[FilteredPackageTable alloc]
5102 initWithBook:book
5103 database:database
5104 title:nil
5105 filter:@selector(isInstalledAndVisible:)
5106 with:[NSNumber numberWithBool:YES]
5107 ];
5108
5109 [self addSubview:packages_];
5110
5111 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5112 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5113 } return self;
5114 }
5115
5116 - (void) resetViewAnimated:(BOOL)animated {
5117 [packages_ resetViewAnimated:animated];
5118 }
5119
5120 - (void) reloadData {
5121 [packages_ reloadData];
5122 }
5123
5124 - (void) _rightButtonClicked {
5125 [packages_ setObject:[NSNumber numberWithBool:expert_]];
5126 [packages_ reloadData];
5127 expert_ = !expert_;
5128 [book_ reloadButtonsForPage:self];
5129 }
5130
5131 - (NSString *) title {
5132 return @"Installed";
5133 }
5134
5135 - (NSString *) backButtonTitle {
5136 return @"Packages";
5137 }
5138
5139 - (id) rightButtonTitle {
5140 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
5141 }
5142
5143 - (UINavigationButtonStyle) rightButtonStyle {
5144 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5145 }
5146
5147 - (void) setDelegate:(id)delegate {
5148 [super setDelegate:delegate];
5149 [packages_ setDelegate:delegate];
5150 }
5151
5152 @end
5153 /* }}} */
5154
5155 /* Home View {{{ */
5156 @interface HomeView : BrowserView {
5157 }
5158
5159 @end
5160
5161 @implementation HomeView
5162
5163 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5164 NSString *context([sheet context]);
5165
5166 if ([context isEqualToString:@"about"])
5167 [sheet dismiss];
5168 else
5169 [super alertSheet:sheet buttonClicked:button];
5170 }
5171
5172 - (void) _leftButtonClicked {
5173 UIActionSheet *sheet = [[[UIActionSheet alloc]
5174 initWithTitle:@"About Cydia Installer"
5175 buttons:[NSArray arrayWithObjects:@"Close", nil]
5176 defaultButtonIndex:0
5177 delegate:self
5178 context:@"about"
5179 ] autorelease];
5180
5181 [sheet setBodyText:
5182 @"Copyright (C) 2008-2009\n"
5183 "Jay Freeman (saurik)\n"
5184 "saurik@saurik.com\n"
5185 "http://www.saurik.com/\n"
5186 "\n"
5187 "The Okori Group\n"
5188 "http://www.theokorigroup.com/\n"
5189 "\n"
5190 "College of Creative Studies,\n"
5191 "University of California,\n"
5192 "Santa Barbara\n"
5193 "http://www.ccs.ucsb.edu/"
5194 ];
5195
5196 [sheet popupAlertAnimated:YES];
5197 }
5198
5199 - (NSString *) leftButtonTitle {
5200 return @"About";
5201 }
5202
5203 @end
5204 /* }}} */
5205 /* Manage View {{{ */
5206 @interface ManageView : BrowserView {
5207 }
5208
5209 @end
5210
5211 @implementation ManageView
5212
5213 - (NSString *) title {
5214 return @"Manage";
5215 }
5216
5217 - (void) _leftButtonClicked {
5218 [delegate_ askForSettings];
5219 }
5220
5221 - (NSString *) leftButtonTitle {
5222 return @"Settings";
5223 }
5224
5225 #if !AlwaysReload
5226 - (id) _rightButtonTitle {
5227 return nil;
5228 }
5229 #endif
5230
5231 - (bool) isLoading {
5232 return false;
5233 }
5234
5235 @end
5236 /* }}} */
5237
5238 #include <BrowserView.m>
5239
5240 /* Cydia Book {{{ */
5241 @interface CYBook : RVBook <
5242 ProgressDelegate
5243 > {
5244 _transient Database *database_;
5245 UINavigationBar *overlay_;
5246 UINavigationBar *underlay_;
5247 UIProgressIndicator *indicator_;
5248 UITextLabel *prompt_;
5249 UIProgressBar *progress_;
5250 UINavigationButton *cancel_;
5251 bool updating_;
5252 }
5253
5254 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
5255 - (void) update;
5256 - (BOOL) updating;
5257
5258 @end
5259
5260 @implementation CYBook
5261
5262 - (void) dealloc {
5263 [overlay_ release];
5264 [indicator_ release];
5265 [prompt_ release];
5266 [progress_ release];
5267 [cancel_ release];
5268 [super dealloc];
5269 }
5270
5271 - (NSString *) getTitleForPage:(RVPage *)page {
5272 return Simplify([super getTitleForPage:page]);
5273 }
5274
5275 - (BOOL) updating {
5276 return updating_;
5277 }
5278
5279 - (void) update {
5280 [UIView beginAnimations:nil context:NULL];
5281
5282 CGRect ovrframe = [overlay_ frame];
5283 ovrframe.origin.y = 0;
5284 [overlay_ setFrame:ovrframe];
5285
5286 CGRect barframe = [navbar_ frame];
5287 barframe.origin.y += ovrframe.size.height;
5288 [navbar_ setFrame:barframe];
5289
5290 CGRect trnframe = [transition_ frame];
5291 trnframe.origin.y += ovrframe.size.height;
5292 trnframe.size.height -= ovrframe.size.height;
5293 [transition_ setFrame:trnframe];
5294
5295 [UIView endAnimations];
5296
5297 [indicator_ startAnimation];
5298 [prompt_ setText:@"Updating Database"];
5299 [progress_ setProgress:0];
5300
5301 updating_ = true;
5302 [overlay_ addSubview:cancel_];
5303
5304 [NSThread
5305 detachNewThreadSelector:@selector(_update)
5306 toTarget:self
5307 withObject:nil
5308 ];
5309 }
5310
5311 - (void) _update_ {
5312 updating_ = false;
5313
5314 [indicator_ stopAnimation];
5315
5316 [UIView beginAnimations:nil context:NULL];
5317
5318 CGRect ovrframe = [overlay_ frame];
5319 ovrframe.origin.y = -ovrframe.size.height;
5320 [overlay_ setFrame:ovrframe];
5321
5322 CGRect barframe = [navbar_ frame];
5323 barframe.origin.y -= ovrframe.size.height;
5324 [navbar_ setFrame:barframe];
5325
5326 CGRect trnframe = [transition_ frame];
5327 trnframe.origin.y -= ovrframe.size.height;
5328 trnframe.size.height += ovrframe.size.height;
5329 [transition_ setFrame:trnframe];
5330
5331 [UIView commitAnimations];
5332
5333 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
5334 }
5335
5336 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5337 if ((self = [super initWithFrame:frame]) != nil) {
5338 database_ = database;
5339
5340 CGRect ovrrect = [navbar_ bounds];
5341 ovrrect.size.height = [UINavigationBar defaultSize].height;
5342 ovrrect.origin.y = -ovrrect.size.height;
5343
5344 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5345 [self addSubview:overlay_];
5346
5347 ovrrect.origin.y = frame.size.height;
5348 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5349 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5350 [self addSubview:underlay_];
5351
5352 [overlay_ setBarStyle:1];
5353 [underlay_ setBarStyle:1];
5354
5355 int barstyle = [overlay_ _barStyle:NO];
5356 bool ugly = barstyle == 0;
5357
5358 UIProgressIndicatorStyle style = ugly ?
5359 UIProgressIndicatorStyleMediumBrown :
5360 UIProgressIndicatorStyleMediumWhite;
5361
5362 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5363 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5364 CGRect indrect = {{indoffset, indoffset}, indsize};
5365
5366 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5367 [indicator_ setStyle:style];
5368 [overlay_ addSubview:indicator_];
5369
5370 CGSize prmsize = {215, indsize.height + 4};
5371
5372 CGRect prmrect = {{
5373 indoffset * 2 + indsize.width,
5374 #ifdef __OBJC2__
5375 -1 +
5376 #endif
5377 unsigned(ovrrect.size.height - prmsize.height) / 2
5378 }, prmsize};
5379
5380 UIFont *font = [UIFont systemFontOfSize:15];
5381
5382 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5383
5384 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5385 [prompt_ setBackgroundColor:[UIColor clearColor]];
5386 [prompt_ setFont:font];
5387
5388 [overlay_ addSubview:prompt_];
5389
5390 CGSize prgsize = {75, 100};
5391
5392 CGRect prgrect = {{
5393 ovrrect.size.width - prgsize.width - 10,
5394 (ovrrect.size.height - prgsize.height) / 2
5395 } , prgsize};
5396
5397 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5398 [progress_ setStyle:0];
5399 [overlay_ addSubview:progress_];
5400
5401 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5402 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5403
5404 CGRect frame = [cancel_ frame];
5405 frame.size.width = 65;
5406 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5407 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5408 [cancel_ setFrame:frame];
5409
5410 [cancel_ setBarStyle:barstyle];
5411 } return self;
5412 }
5413
5414 - (void) _onCancel {
5415 updating_ = false;
5416 [cancel_ removeFromSuperview];
5417 }
5418
5419 - (void) _update { _pooled
5420 Status status;
5421 status.setDelegate(self);
5422
5423 [database_ updateWithStatus:status];
5424
5425 [self
5426 performSelectorOnMainThread:@selector(_update_)
5427 withObject:nil
5428 waitUntilDone:NO
5429 ];
5430 }
5431
5432 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5433 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5434 }
5435
5436 - (void) setProgressTitle:(NSString *)title {
5437 [self
5438 performSelectorOnMainThread:@selector(_setProgressTitle:)
5439 withObject:title
5440 waitUntilDone:YES
5441 ];
5442 }
5443
5444 - (void) setProgressPercent:(float)percent {
5445 [self
5446 performSelectorOnMainThread:@selector(_setProgressPercent:)
5447 withObject:[NSNumber numberWithFloat:percent]
5448 waitUntilDone:YES
5449 ];
5450 }
5451
5452 - (void) startProgress {
5453 }
5454
5455 - (void) addProgressOutput:(NSString *)output {
5456 [self
5457 performSelectorOnMainThread:@selector(_addProgressOutput:)
5458 withObject:output
5459 waitUntilDone:YES
5460 ];
5461 }
5462
5463 - (bool) isCancelling:(size_t)received {
5464 return !updating_;
5465 }
5466
5467 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5468 [sheet dismiss];
5469 }
5470
5471 - (void) _setProgressTitle:(NSString *)title {
5472 [prompt_ setText:title];
5473 }
5474
5475 - (void) _setProgressPercent:(NSNumber *)percent {
5476 [progress_ setProgress:[percent floatValue]];
5477 }
5478
5479 - (void) _addProgressOutput:(NSString *)output {
5480 }
5481
5482 @end
5483 /* }}} */
5484 /* Cydia:// Protocol {{{ */
5485 @interface CydiaURLProtocol : NSURLProtocol {
5486 }
5487
5488 @end
5489
5490 @implementation CydiaURLProtocol
5491
5492 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
5493 NSURL *url([request URL]);
5494 if (url == nil)
5495 return NO;
5496 NSString *scheme([[url scheme] lowercaseString]);
5497 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5498 return NO;
5499 return YES;
5500 }
5501
5502 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5503 return request;
5504 }
5505
5506 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5507 id<NSURLProtocolClient> client([self client]);
5508 if (icon == nil)
5509 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
5510 else {
5511 NSData *data(UIImagePNGRepresentation(icon));
5512
5513 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5514 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5515 [client URLProtocol:self didLoadData:data];
5516 [client URLProtocolDidFinishLoading:self];
5517 }
5518 }
5519
5520 - (void) startLoading {
5521 id<NSURLProtocolClient> client([self client]);
5522 NSURLRequest *request([self request]);
5523
5524 NSURL *url([request URL]);
5525 NSString *href([url absoluteString]);
5526
5527 NSString *path([href substringFromIndex:8]);
5528 NSRange slash([path rangeOfString:@"/"]);
5529
5530 NSString *command;
5531 if (slash.location == NSNotFound) {
5532 command = path;
5533 path = nil;
5534 } else {
5535 command = [path substringToIndex:slash.location];
5536 path = [path substringFromIndex:(slash.location + 1)];
5537 }
5538
5539 Database *database([Database sharedInstance]);
5540
5541 if ([command isEqualToString:@"package-icon"]) {
5542 if (path == nil)
5543 goto fail;
5544 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5545 Package *package([database packageWithName:path]);
5546 if (package == nil)
5547 goto fail;
5548 UIImage *icon([package icon]);
5549 [self _returnPNGWithImage:icon forRequest:request];
5550 } else if ([command isEqualToString:@"source-icon"]) {
5551 if (path == nil)
5552 goto fail;
5553 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5554 NSString *source(Simplify(path));
5555 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5556 if (icon == nil)
5557 icon = [UIImage applicationImageNamed:@"unknown.png"];
5558 [self _returnPNGWithImage:icon forRequest:request];
5559 } else if ([command isEqualToString:@"uikit-image"]) {
5560 if (path == nil)
5561 goto fail;
5562 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5563 UIImage *icon(_UIImageWithName(path));
5564 [self _returnPNGWithImage:icon forRequest:request];
5565 } else if ([command isEqualToString:@"section-icon"]) {
5566 if (path == nil)
5567 goto fail;
5568 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5569 NSString *section(Simplify(path));
5570 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5571 if (icon == nil)
5572 icon = [UIImage applicationImageNamed:@"unknown.png"];
5573 [self _returnPNGWithImage:icon forRequest:request];
5574 } else fail: {
5575 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5576 }
5577 }
5578
5579 - (void) stopLoading {
5580 }
5581
5582 @end
5583 /* }}} */
5584
5585 /* Sections View {{{ */
5586 @interface SectionsView : RVPage {
5587 _transient Database *database_;
5588 NSMutableArray *sections_;
5589 NSMutableArray *filtered_;
5590 UITransitionView *transition_;
5591 UITable *list_;
5592 UIView *accessory_;
5593 BOOL editing_;
5594 }
5595
5596 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5597 - (void) reloadData;
5598 - (void) resetView;
5599
5600 @end
5601
5602 @implementation SectionsView
5603
5604 - (void) dealloc {
5605 [list_ setDataSource:nil];
5606 [list_ setDelegate:nil];
5607
5608 [sections_ release];
5609 [filtered_ release];
5610 [transition_ release];
5611 [list_ release];
5612 [accessory_ release];
5613 [super dealloc];
5614 }
5615
5616 - (int) numberOfRowsInTable:(UITable *)table {
5617 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5618 }
5619
5620 - (float) table:(UITable *)table heightForRow:(int)row {
5621 return 45;
5622 }
5623
5624 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5625 if (reusing == nil)
5626 reusing = [[[SectionCell alloc] init] autorelease];
5627 [(SectionCell *)reusing setSection:(editing_ ?
5628 [sections_ objectAtIndex:row] :
5629 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5630 ) editing:editing_];
5631 return reusing;
5632 }
5633
5634 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5635 return !editing_;
5636 }
5637
5638 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
5639 return !editing_;
5640 }
5641
5642 - (void) tableRowSelected:(NSNotification *)notification {
5643 int row = [[notification object] selectedRow];
5644 if (row == INT_MAX)
5645 return;
5646
5647 Section *section;
5648 NSString *name;
5649 NSString *title;
5650
5651 if (row == 0) {
5652 section = nil;
5653 name = nil;
5654 title = @"All Packages";
5655 } else {
5656 section = [filtered_ objectAtIndex:(row - 1)];
5657 name = [section name];
5658
5659 if (name != nil)
5660 title = name;
5661 else {
5662 name = @"";
5663 title = @"(No Section)";
5664 }
5665 }
5666
5667 PackageTable *table = [[[FilteredPackageTable alloc]
5668 initWithBook:book_
5669 database:database_
5670 title:title
5671 filter:@selector(isVisiblyUninstalledInSection:)
5672 with:name
5673 ] autorelease];
5674
5675 [table setDelegate:delegate_];
5676
5677 [book_ pushPage:table];
5678 }
5679
5680 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5681 if ((self = [super initWithBook:book]) != nil) {
5682 database_ = database;
5683
5684 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5685 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5686
5687 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5688 [self addSubview:transition_];
5689
5690 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5691 [transition_ transition:0 toView:list_];
5692
5693 UITableColumn *column = [[[UITableColumn alloc]
5694 initWithTitle:@"Name"
5695 identifier:@"name"
5696 width:[self frame].size.width
5697 ] autorelease];
5698
5699 [list_ setDataSource:self];
5700 [list_ setSeparatorStyle:1];
5701 [list_ addTableColumn:column];
5702 [list_ setDelegate:self];
5703 [list_ setReusesTableCells:YES];
5704
5705 [self reloadData];
5706
5707 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5708 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5709 } return self;
5710 }
5711
5712 - (void) reloadData {
5713 NSArray *packages = [database_ packages];
5714
5715 [sections_ removeAllObjects];
5716 [filtered_ removeAllObjects];
5717
5718 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5719 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5720
5721 _trace();
5722 for (Package *package in packages) {
5723 NSString *name([package section]);
5724
5725 if (name != nil) {
5726 Section *section([sections objectForKey:name]);
5727 if (section == nil) {
5728 section = [[[Section alloc] initWithName:name] autorelease];
5729 [sections setObject:section forKey:name];
5730 }
5731 }
5732
5733 if ([package valid] && [package installed] == nil && [package visible])
5734 [filtered addObject:package];
5735 }
5736 _trace();
5737
5738 [sections_ addObjectsFromArray:[sections allValues]];
5739 [sections_ sortUsingSelector:@selector(compareByName:)];
5740
5741 _trace();
5742 [filtered sortUsingSelector:@selector(compareBySection:)];
5743 _trace();
5744
5745 Section *section = nil;
5746 for (Package *package in filtered) {
5747 NSString *name = [package section];
5748
5749 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
5750 section = name == nil ?
5751 [[[Section alloc] initWithName:nil] autorelease] :
5752 [sections objectForKey:name];
5753 [filtered_ addObject:section];
5754 }
5755
5756 [section addToCount];
5757 }
5758 _trace();
5759
5760 [list_ reloadData];
5761 _trace();
5762 }
5763
5764 - (void) resetView {
5765 if (editing_)
5766 [self _rightButtonClicked];
5767 }
5768
5769 - (void) resetViewAnimated:(BOOL)animated {
5770 [list_ resetViewAnimated:animated];
5771 }
5772
5773 - (void) _rightButtonClicked {
5774 if ((editing_ = !editing_))
5775 [list_ reloadData];
5776 else
5777 [delegate_ updateData];
5778 [book_ reloadTitleForPage:self];
5779 [book_ reloadButtonsForPage:self];
5780 }
5781
5782 - (NSString *) title {
5783 return editing_ ? @"Section Visibility" : @"Install by Section";
5784 }
5785
5786 - (NSString *) backButtonTitle {
5787 return @"Sections";
5788 }
5789
5790 - (id) rightButtonTitle {
5791 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5792 }
5793
5794 - (UINavigationButtonStyle) rightButtonStyle {
5795 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5796 }
5797
5798 - (UIView *) accessoryView {
5799 return accessory_;
5800 }
5801
5802 @end
5803 /* }}} */
5804 /* Changes View {{{ */
5805 @interface ChangesView : RVPage {
5806 _transient Database *database_;
5807 NSMutableArray *packages_;
5808 NSMutableArray *sections_;
5809 UISectionList *list_;
5810 unsigned upgrades_;
5811 }
5812
5813 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5814 - (void) reloadData;
5815
5816 @end
5817
5818 @implementation ChangesView
5819
5820 - (void) dealloc {
5821 [[list_ table] setDelegate:nil];
5822 [list_ setDataSource:nil];
5823
5824 [packages_ release];
5825 [sections_ release];
5826 [list_ release];
5827 [super dealloc];
5828 }
5829
5830 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
5831 return [sections_ count];
5832 }
5833
5834 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5835 return [[sections_ objectAtIndex:section] name];
5836 }
5837
5838 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5839 return [[sections_ objectAtIndex:section] row];
5840 }
5841
5842 - (int) numberOfRowsInTable:(UITable *)table {
5843 return [packages_ count];
5844 }
5845
5846 - (float) table:(UITable *)table heightForRow:(int)row {
5847 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
5848 }
5849
5850 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5851 if (reusing == nil)
5852 reusing = [[[PackageCell alloc] init] autorelease];
5853 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5854 return reusing;
5855 }
5856
5857 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5858 return NO;
5859 }
5860
5861 - (void) tableRowSelected:(NSNotification *)notification {
5862 int row = [[notification object] selectedRow];
5863 if (row == INT_MAX)
5864 return;
5865 Package *package = [packages_ objectAtIndex:row];
5866 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5867 [view setDelegate:delegate_];
5868 [view setPackage:package];
5869 [book_ pushPage:view];
5870 }
5871
5872 - (void) _leftButtonClicked {
5873 [(CYBook *)book_ update];
5874 [self reloadButtons];
5875 }
5876
5877 - (void) _rightButtonClicked {
5878 [delegate_ distUpgrade];
5879 }
5880
5881 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5882 if ((self = [super initWithBook:book]) != nil) {
5883 database_ = database;
5884
5885 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5886 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5887
5888 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5889 [self addSubview:list_];
5890
5891 [list_ setShouldHideHeaderInShortLists:NO];
5892 [list_ setDataSource:self];
5893 //[list_ setSectionListStyle:1];
5894
5895 UITableColumn *column = [[[UITableColumn alloc]
5896 initWithTitle:@"Name"
5897 identifier:@"name"
5898 width:[self frame].size.width
5899 ] autorelease];
5900
5901 UITable *table = [list_ table];
5902 [table setSeparatorStyle:1];
5903 [table addTableColumn:column];
5904 [table setDelegate:self];
5905 [table setReusesTableCells:YES];
5906
5907 [self reloadData];
5908
5909 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5910 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5911 } return self;
5912 }
5913
5914 - (void) reloadData {
5915 NSArray *packages = [database_ packages];
5916
5917 [packages_ removeAllObjects];
5918 [sections_ removeAllObjects];
5919
5920 _trace();
5921 for (Package *package in packages)
5922 if (
5923 [package installed] == nil && [package valid] && [package visible] ||
5924 [package upgradableAndEssential:YES]
5925 )
5926 [packages_ addObject:package];
5927
5928 _trace();
5929 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
5930 _trace();
5931
5932 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
5933 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
5934 Section *section = nil;
5935 NSDate *last = nil;
5936
5937 upgrades_ = 0;
5938 bool unseens = false;
5939
5940 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
5941
5942 _trace();
5943 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5944 Package *package = [packages_ objectAtIndex:offset];
5945
5946 if (![package upgradableAndEssential:YES]) {
5947 unseens = true;
5948 NSDate *seen = [package seen];
5949
5950 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5951 last = seen;
5952
5953 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
5954 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5955 [sections_ addObject:section];
5956 [name release];
5957 }
5958
5959 [section addToCount];
5960 } else if ([package ignored])
5961 [ignored addToCount];
5962 else {
5963 ++upgrades_;
5964 [upgradable addToCount];
5965 }
5966 }
5967 _trace();
5968
5969 CFRelease(formatter);
5970
5971 if (unseens) {
5972 Section *last = [sections_ lastObject];
5973 size_t count = [last count];
5974 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5975 [sections_ removeLastObject];
5976 }
5977
5978 if ([ignored count] != 0)
5979 [sections_ insertObject:ignored atIndex:0];
5980 if (upgrades_ != 0)
5981 [sections_ insertObject:upgradable atIndex:0];
5982
5983 [list_ reloadData];
5984 [self reloadButtons];
5985 }
5986
5987 - (void) resetViewAnimated:(BOOL)animated {
5988 [list_ resetViewAnimated:animated];
5989 }
5990
5991 - (NSString *) leftButtonTitle {
5992 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5993 }
5994
5995 - (id) rightButtonTitle {
5996 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5997 }
5998
5999 - (NSString *) title {
6000 return @"Changes";
6001 }
6002
6003 @end
6004 /* }}} */
6005 /* Search View {{{ */
6006 @protocol SearchViewDelegate
6007 - (void) showKeyboard:(BOOL)show;
6008 @end
6009
6010 @interface SearchView : RVPage {
6011 UIView *accessory_;
6012 UISearchField *field_;
6013 UITransitionView *transition_;
6014 FilteredPackageTable *table_;
6015 UIPreferencesTable *advanced_;
6016 UIView *dimmed_;
6017 bool flipped_;
6018 bool reload_;
6019 }
6020
6021 - (id) initWithBook:(RVBook *)book database:(Database *)database;
6022 - (void) reloadData;
6023
6024 @end
6025
6026 @implementation SearchView
6027
6028 - (void) dealloc {
6029 [field_ setDelegate:nil];
6030
6031 [accessory_ release];
6032 [field_ release];
6033 [transition_ release];
6034 [table_ release];
6035 [advanced_ release];
6036 [dimmed_ release];
6037 [super dealloc];
6038 }
6039
6040 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6041 return 1;
6042 }
6043
6044 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6045 switch (group) {
6046 case 0: return @"Advanced Search (Coming Soon!)";
6047
6048 default: _assert(false);
6049 }
6050 }
6051
6052 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6053 switch (group) {
6054 case 0: return 0;
6055
6056 default: _assert(false);
6057 }
6058 }
6059
6060 - (void) _showKeyboard:(BOOL)show {
6061 CGSize keysize = [UIKeyboard defaultSize];
6062 CGRect keydown = [book_ pageBounds];
6063 CGRect keyup = keydown;
6064 keyup.size.height -= keysize.height - ButtonBarHeight_;
6065
6066 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6067
6068 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6069 [animation setSignificantRectFields:8];
6070
6071 if (show) {
6072 [animation setStartFrame:keydown];
6073 [animation setEndFrame:keyup];
6074 } else {
6075 [animation setStartFrame:keyup];
6076 [animation setEndFrame:keydown];
6077 }
6078
6079 UIAnimator *animator = [UIAnimator sharedAnimator];
6080
6081 [animator
6082 addAnimations:[NSArray arrayWithObjects:animation, nil]
6083 withDuration:(KeyboardTime_ - delay)
6084 start:!show
6085 ];
6086
6087 if (show)
6088 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6089
6090 [delegate_ showKeyboard:show];
6091 }
6092
6093 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
6094 [self _showKeyboard:YES];
6095 }
6096
6097 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
6098 [self _showKeyboard:NO];
6099 }
6100
6101 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
6102 if (reload_) {
6103 NSString *text([field_ text]);
6104 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6105 [self reloadData];
6106 reload_ = false;
6107 }
6108 }
6109
6110 - (void) textFieldClearButtonPressed:(UITextField *)field {
6111 reload_ = true;
6112 }
6113
6114 - (void) keyboardInputShouldDelete:(id)input {
6115 reload_ = true;
6116 }
6117
6118 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
6119 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6120 reload_ = true;
6121 return YES;
6122 } else {
6123 [field_ resignFirstResponder];
6124 return NO;
6125 }
6126 }
6127
6128 - (id) initWithBook:(RVBook *)book database:(Database *)database {
6129 if ((self = [super initWithBook:book]) != nil) {
6130 CGRect pageBounds = [book_ pageBounds];
6131
6132 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6133 [self addSubview:transition_];
6134
6135 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6136
6137 [advanced_ setReusesTableCells:YES];
6138 [advanced_ setDataSource:self];
6139 [advanced_ reloadData];
6140
6141 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6142 CGColor dimmed(space_, 0, 0, 0, 0.5);
6143 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
6144
6145 table_ = [[FilteredPackageTable alloc]
6146 initWithBook:book
6147 database:database
6148 title:nil
6149 filter:@selector(isUnfilteredAndSearchedForBy:)
6150 with:nil
6151 ];
6152
6153 [table_ setShouldHideHeaderInShortLists:NO];
6154 [transition_ transition:0 toView:table_];
6155
6156 CGRect cnfrect = {{
6157 #ifdef __OBJC2__
6158 6 +
6159 #endif
6160 1, 38}, {17, 18}};
6161
6162 CGRect area;
6163 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
6164 area.origin.y = 1;
6165
6166 area.size.width =
6167 #ifdef __OBJC2__
6168 8 +
6169 #endif
6170 [self bounds].size.width - area.origin.x - 18;
6171
6172 area.size.height = [UISearchField defaultHeight];
6173
6174 field_ = [[UISearchField alloc] initWithFrame:area];
6175
6176 UIFont *font = [UIFont systemFontOfSize:16];
6177 [field_ setFont:font];
6178
6179 [field_ setPlaceholder:@"Package Names & Descriptions"];
6180 [field_ setDelegate:self];
6181
6182 [field_ setPaddingTop:5];
6183
6184 UITextInputTraits *traits([field_ textInputTraits]);
6185 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6186 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6187 [traits setReturnKeyType:UIReturnKeySearch];
6188
6189 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
6190
6191 accessory_ = [[UIView alloc] initWithFrame:accrect];
6192 [accessory_ addSubview:field_];
6193
6194 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
6195 [configure setShowPressFeedback:YES];
6196 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
6197 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6198 [accessory_ addSubview:configure];*/
6199
6200 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6201 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6202 } return self;
6203 }
6204
6205 - (void) flipPage {
6206 #ifndef __OBJC2__
6207 LKAnimation *animation = [LKTransition animation];
6208 [animation setType:@"oglFlip"];
6209 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6210 [animation setFillMode:@"extended"];
6211 [animation setTransitionFlags:3];
6212 [animation setDuration:10];
6213 [animation setSpeed:0.35];
6214 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6215 [[transition_ _layer] addAnimation:animation forKey:0];
6216 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6217 flipped_ = !flipped_;
6218 #endif
6219 }
6220
6221 - (void) configurePushed {
6222 [field_ resignFirstResponder];
6223 [self flipPage];
6224 }
6225
6226 - (void) resetViewAnimated:(BOOL)animated {
6227 if (flipped_)
6228 [self flipPage];
6229 [table_ resetViewAnimated:animated];
6230 }
6231
6232 - (void) _reloadData {
6233 }
6234
6235 - (void) reloadData {
6236 if (flipped_)
6237 [self flipPage];
6238 [table_ setObject:[field_ text]];
6239 _profile(SearchView$reloadData)
6240 [table_ reloadData];
6241 _end
6242 PrintTimes();
6243 [table_ resetCursor];
6244 }
6245
6246 - (UIView *) accessoryView {
6247 return accessory_;
6248 }
6249
6250 - (NSString *) title {
6251 return nil;
6252 }
6253
6254 - (NSString *) backButtonTitle {
6255 return @"Search";
6256 }
6257
6258 - (void) setDelegate:(id)delegate {
6259 [table_ setDelegate:delegate];
6260 [super setDelegate:delegate];
6261 }
6262
6263 @end
6264 /* }}} */
6265
6266 @interface SettingsView : RVPage {
6267 _transient Database *database_;
6268 NSString *name_;
6269 Package *package_;
6270 UIPreferencesTable *table_;
6271 _UISwitchSlider *subscribedSwitch_;
6272 _UISwitchSlider *ignoredSwitch_;
6273 UIPreferencesControlTableCell *subscribedCell_;
6274 UIPreferencesControlTableCell *ignoredCell_;
6275 }
6276
6277 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6278
6279 @end
6280
6281 @implementation SettingsView
6282
6283 - (void) dealloc {
6284 [table_ setDataSource:nil];
6285
6286 [name_ release];
6287 if (package_ != nil)
6288 [package_ release];
6289 [table_ release];
6290 [subscribedSwitch_ release];
6291 [ignoredSwitch_ release];
6292 [subscribedCell_ release];
6293 [ignoredCell_ release];
6294 [super dealloc];
6295 }
6296
6297 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6298 if (package_ == nil)
6299 return 0;
6300
6301 return 2;
6302 }
6303
6304 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6305 if (package_ == nil)
6306 return nil;
6307
6308 switch (group) {
6309 case 0: return nil;
6310 case 1: return nil;
6311
6312 default: _assert(false);
6313 }
6314
6315 return nil;
6316 }
6317
6318 - (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6319 if (package_ == nil)
6320 return NO;
6321
6322 switch (group) {
6323 case 0: return NO;
6324 case 1: return YES;
6325
6326 default: _assert(false);
6327 }
6328
6329 return NO;
6330 }
6331
6332 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6333 if (package_ == nil)
6334 return 0;
6335
6336 switch (group) {
6337 case 0: return 1;
6338 case 1: return 1;
6339
6340 default: _assert(false);
6341 }
6342
6343 return 0;
6344 }
6345
6346 - (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6347 if (package_ == nil)
6348 return;
6349
6350 _UISwitchSlider *slider([cell control]);
6351 BOOL value([slider value] != 0);
6352 NSMutableDictionary *metadata([package_ metadata]);
6353
6354 BOOL before;
6355 if (NSNumber *number = [metadata objectForKey:key])
6356 before = [number boolValue];
6357 else
6358 before = NO;
6359
6360 if (value != before) {
6361 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6362 Changed_ = true;
6363 [delegate_ updateData];
6364 }
6365 }
6366
6367 - (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6368 [self onSomething:cell withKey:@"IsSubscribed"];
6369 }
6370
6371 - (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6372 [self onSomething:cell withKey:@"IsIgnored"];
6373 }
6374
6375 - (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6376 if (package_ == nil)
6377 return nil;
6378
6379 switch (group) {
6380 case 0: switch (row) {
6381 case 0:
6382 return subscribedCell_;
6383 case 1:
6384 return ignoredCell_;
6385 default: _assert(false);
6386 } break;
6387
6388 case 1: switch (row) {
6389 case 0: {
6390 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6391 [cell setShowSelection:NO];
6392 [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."];
6393 return cell;
6394 }
6395
6396 default: _assert(false);
6397 } break;
6398
6399 default: _assert(false);
6400 }
6401
6402 return nil;
6403 }
6404
6405 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6406 if ((self = [super initWithBook:book])) {
6407 database_ = database;
6408 name_ = [package retain];
6409
6410 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6411 [self addSubview:table_];
6412
6413 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6414 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
6415
6416 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6417 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
6418
6419 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6420 [subscribedCell_ setShowSelection:NO];
6421 [subscribedCell_ setTitle:@"Show All Changes"];
6422 [subscribedCell_ setControl:subscribedSwitch_];
6423
6424 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6425 [ignoredCell_ setShowSelection:NO];
6426 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6427 [ignoredCell_ setControl:ignoredSwitch_];
6428
6429 [table_ setDataSource:self];
6430 [self reloadData];
6431 } return self;
6432 }
6433
6434 - (void) resetViewAnimated:(BOOL)animated {
6435 [table_ resetViewAnimated:animated];
6436 }
6437
6438 - (void) reloadData {
6439 if (package_ != nil)
6440 [package_ autorelease];
6441 package_ = [database_ packageWithName:name_];
6442 if (package_ != nil) {
6443 [package_ retain];
6444 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6445 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
6446 }
6447
6448 [table_ reloadData];
6449 }
6450
6451 - (NSString *) title {
6452 return @"Settings";
6453 }
6454
6455 @end
6456
6457 /* Signature View {{{ */
6458 @interface SignatureView : BrowserView {
6459 _transient Database *database_;
6460 NSString *package_;
6461 }
6462
6463 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6464
6465 @end
6466
6467 @implementation SignatureView
6468
6469 - (void) dealloc {
6470 [package_ release];
6471 [super dealloc];
6472 }
6473
6474 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6475 // XXX: dude!
6476 [super webView:sender didClearWindowObject:window forFrame:frame];
6477 }
6478
6479 - (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6480 if ((self = [super initWithBook:book]) != nil) {
6481 database_ = database;
6482 package_ = [package retain];
6483 [self reloadData];
6484 } return self;
6485 }
6486
6487 - (void) resetViewAnimated:(BOOL)animated {
6488 }
6489
6490 - (void) reloadData {
6491 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6492 }
6493
6494 @end
6495 /* }}} */
6496
6497 @interface Cydia : UIApplication <
6498 ConfirmationViewDelegate,
6499 ProgressViewDelegate,
6500 SearchViewDelegate,
6501 CydiaDelegate
6502 > {
6503 UIWindow *window_;
6504
6505 UIView *underlay_;
6506 UIView *overlay_;
6507 CYBook *book_;
6508 UIToolbar *buttonbar_;
6509
6510 RVBook *confirm_;
6511
6512 NSMutableArray *essential_;
6513 NSMutableArray *broken_;
6514
6515 Database *database_;
6516 ProgressView *progress_;
6517
6518 unsigned tag_;
6519
6520 UIKeyboard *keyboard_;
6521 UIProgressHUD *hud_;
6522
6523 SectionsView *sections_;
6524 ChangesView *changes_;
6525 ManageView *manage_;
6526 SearchView *search_;
6527 }
6528
6529 @end
6530
6531 @implementation Cydia
6532
6533 - (void) _loaded {
6534 if ([broken_ count] != 0) {
6535 int count = [broken_ count];
6536
6537 UIActionSheet *sheet = [[[UIActionSheet alloc]
6538 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6539 buttons:[NSArray arrayWithObjects:
6540 @"Forcibly Clear",
6541 @"Ignore (Temporary)",
6542 nil]
6543 defaultButtonIndex:0
6544 delegate:self
6545 context:@"fixhalf"
6546 ] autorelease];
6547
6548 [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."];
6549 [sheet popupAlertAnimated:YES];
6550 } else if (!Ignored_ && [essential_ count] != 0) {
6551 int count = [essential_ count];
6552
6553 UIActionSheet *sheet = [[[UIActionSheet alloc]
6554 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
6555 buttons:[NSArray arrayWithObjects:
6556 @"Upgrade Essential",
6557 @"Complete Upgrade",
6558 @"Ignore (Temporary)",
6559 nil]
6560 defaultButtonIndex:0
6561 delegate:self
6562 context:@"upgrade"
6563 ] autorelease];
6564
6565 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
6566 [sheet popupAlertAnimated:YES];
6567 }
6568 }
6569
6570 - (void) _reloadData {
6571 UIView *block();
6572
6573 static bool loaded(false);
6574 UIProgressHUD *hud([self addProgressHUD]);
6575 [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
6576 loaded = true;
6577
6578 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
6579 _trace();
6580
6581 [self removeProgressHUD:hud];
6582
6583 size_t changes(0);
6584
6585 [essential_ removeAllObjects];
6586 [broken_ removeAllObjects];
6587
6588 NSArray *packages = [database_ packages];
6589 for (Package *package in packages) {
6590 if ([package half])
6591 [broken_ addObject:package];
6592 if ([package upgradableAndEssential:NO]) {
6593 if ([package essential])
6594 [essential_ addObject:package];
6595 ++changes;
6596 }
6597 }
6598
6599 if (changes != 0) {
6600 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
6601 [buttonbar_ setBadgeValue:badge forButton:3];
6602 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6603 [buttonbar_ setBadgeAnimated:YES forButton:3];
6604 [self setApplicationBadge:badge];
6605 } else {
6606 [buttonbar_ setBadgeValue:nil forButton:3];
6607 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6608 [buttonbar_ setBadgeAnimated:NO forButton:3];
6609 [self removeApplicationBadge];
6610 }
6611
6612 [self updateData];
6613
6614 // XXX: what is this line of code for?
6615 if ([packages count] == 0);
6616 else if (Loaded_ || ManualRefresh) loaded:
6617 [self _loaded];
6618 else {
6619 Loaded_ = YES;
6620
6621 if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
6622 NSTimeInterval interval([update timeIntervalSinceNow]);
6623 if (interval <= 0 && interval > -600)
6624 goto loaded;
6625 }
6626
6627 [book_ update];
6628 }
6629 }
6630
6631 - (void) _saveConfig {
6632 if (Changed_) {
6633 _trace();
6634 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
6635 _trace();
6636 Changed_ = false;
6637 }
6638 }
6639
6640 - (void) updateData {
6641 [self _saveConfig];
6642
6643 /* XXX: this is just stupid */
6644 if (tag_ != 2 && sections_ != nil)
6645 [sections_ reloadData];
6646 if (tag_ != 3 && changes_ != nil)
6647 [changes_ reloadData];
6648 if (tag_ != 5 && search_ != nil)
6649 [search_ reloadData];
6650
6651 [book_ reloadData];
6652 }
6653
6654 - (void) update_ {
6655 [database_ update];
6656 }
6657
6658 - (void) syncData {
6659 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6660 _assert(file != NULL);
6661
6662 NSArray *keys = [Sources_ allKeys];
6663
6664 for (NSString *key in keys) {
6665 NSDictionary *source = [Sources_ objectForKey:key];
6666
6667 fprintf(file, "%s %s %s\n",
6668 [[source objectForKey:@"Type"] UTF8String],
6669 [[source objectForKey:@"URI"] UTF8String],
6670 [[source objectForKey:@"Distribution"] UTF8String]
6671 );
6672 }
6673
6674 fclose(file);
6675
6676 [self _saveConfig];
6677
6678 [progress_
6679 detachNewThreadSelector:@selector(update_)
6680 toTarget:self
6681 withObject:nil
6682 title:@"Updating Sources"
6683 ];
6684 }
6685
6686 - (void) reloadData {
6687 @synchronized (self) {
6688 if (confirm_ == nil)
6689 [self _reloadData];
6690 }
6691 }
6692
6693 - (void) resolve {
6694 pkgProblemResolver *resolver = [database_ resolver];
6695
6696 resolver->InstallProtect();
6697 if (!resolver->Resolve(true))
6698 _error->Discard();
6699 }
6700
6701 - (void) popUpBook:(RVBook *)book {
6702 [underlay_ popSubview:book];
6703 }
6704
6705 - (CGRect) popUpBounds {
6706 return [underlay_ bounds];
6707 }
6708
6709 - (void) perform {
6710 [database_ prepare];
6711
6712 confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
6713 [confirm_ setDelegate:self];
6714
6715 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6716 [page setDelegate:self];
6717
6718 [confirm_ setPage:page];
6719 [self popUpBook:confirm_];
6720 }
6721
6722 - (void) installPackage:(Package *)package {
6723 @synchronized (self) {
6724 [package install];
6725 [self resolve];
6726 [self perform];
6727 }
6728 }
6729
6730 - (void) removePackage:(Package *)package {
6731 @synchronized (self) {
6732 [package remove];
6733 [self resolve];
6734 [self perform];
6735 }
6736 }
6737
6738 - (void) distUpgrade {
6739 @synchronized (self) {
6740 [database_ upgrade];
6741 [self perform];
6742 }
6743 }
6744
6745 - (void) cancel {
6746 @synchronized (self) {
6747 [self _reloadData];
6748 if (confirm_ != nil) {
6749 [confirm_ release];
6750 confirm_ = nil;
6751 }
6752 }
6753 }
6754
6755 - (void) confirm {
6756 [overlay_ removeFromSuperview];
6757 reload_ = true;
6758
6759 [progress_
6760 detachNewThreadSelector:@selector(perform)
6761 toTarget:database_
6762 withObject:nil
6763 title:@"Running"
6764 ];
6765 }
6766
6767 - (void) bootstrap_ {
6768 [database_ update];
6769 [database_ upgrade];
6770 [database_ prepare];
6771 [database_ perform];
6772 }
6773
6774 - (void) bootstrap {
6775 [progress_
6776 detachNewThreadSelector:@selector(bootstrap_)
6777 toTarget:self
6778 withObject:nil
6779 title:@"Bootstrap Install"
6780 ];
6781 }
6782
6783 - (void) progressViewIsComplete:(ProgressView *)progress {
6784 if (confirm_ != nil) {
6785 [underlay_ addSubview:overlay_];
6786 [confirm_ popFromSuperviewAnimated:NO];
6787 }
6788
6789 [self cancel];
6790 }
6791
6792 - (void) setPage:(RVPage *)page {
6793 [page resetViewAnimated:NO];
6794 [page setDelegate:self];
6795 [book_ setPage:page];
6796 }
6797
6798 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
6799 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
6800 [browser loadURL:url];
6801 return browser;
6802 }
6803
6804 - (void) _setHomePage {
6805 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6806 }
6807
6808 - (void) buttonBarItemTapped:(id)sender {
6809 unsigned tag = [sender tag];
6810 if (tag == tag_) {
6811 [book_ resetViewAnimated:YES];
6812 return;
6813 } else if (tag_ == 2 && tag != 2)
6814 [sections_ resetView];
6815
6816 switch (tag) {
6817 case 1: [self _setHomePage]; break;
6818
6819 case 2: [self setPage:sections_]; break;
6820 case 3: [self setPage:changes_]; break;
6821 case 4: [self setPage:manage_]; break;
6822 case 5: [self setPage:search_]; break;
6823
6824 default: _assert(false);
6825 }
6826
6827 tag_ = tag;
6828 }
6829
6830 - (void) applicationWillSuspend {
6831 [database_ clean];
6832 [super applicationWillSuspend];
6833 }
6834
6835 - (void) askForSettings {
6836 UIActionSheet *role = [[[UIActionSheet alloc]
6837 initWithTitle:@"Who Are You?"
6838 buttons:[NSArray arrayWithObjects:
6839 @"User (Graphical Only)",
6840 @"Hacker (+ Command Line)",
6841 @"Developer (No Filters)",
6842 nil]
6843 defaultButtonIndex:-1
6844 delegate:self
6845 context:@"role"
6846 ] autorelease];
6847
6848 [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."];
6849 [role popupAlertAnimated:YES];
6850 }
6851
6852 - (void) finish {
6853 if (hud_ != nil) {
6854 [self setStatusBarShowsProgress:NO];
6855 [self removeProgressHUD:hud_];
6856
6857 [hud_ autorelease];
6858 hud_ = nil;
6859
6860 pid_t pid = ExecFork();
6861 if (pid == 0) {
6862 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6863 perror("launchctl stop");
6864 }
6865
6866 return;
6867 }
6868
6869 if (Role_ == nil) {
6870 [self askForSettings];
6871 return;
6872 }
6873
6874 _trace();
6875 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
6876
6877 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6878 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6879 0, 0, screenrect.size.width, screenrect.size.height - 48
6880 ) database:database_];
6881
6882 [book_ setDelegate:self];
6883
6884 [overlay_ addSubview:book_];
6885
6886 NSArray *buttonitems = [NSArray arrayWithObjects:
6887 [NSDictionary dictionaryWithObjectsAndKeys:
6888 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6889 @"home-up.png", kUIButtonBarButtonInfo,
6890 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
6891 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6892 self, kUIButtonBarButtonTarget,
6893 @"Home", kUIButtonBarButtonTitle,
6894 @"0", kUIButtonBarButtonType,
6895 nil],
6896
6897 [NSDictionary dictionaryWithObjectsAndKeys:
6898 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6899 @"install-up.png", kUIButtonBarButtonInfo,
6900 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6901 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6902 self, kUIButtonBarButtonTarget,
6903 @"Sections", kUIButtonBarButtonTitle,
6904 @"0", kUIButtonBarButtonType,
6905 nil],
6906
6907 [NSDictionary dictionaryWithObjectsAndKeys:
6908 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6909 @"changes-up.png", kUIButtonBarButtonInfo,
6910 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
6911 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6912 self, kUIButtonBarButtonTarget,
6913 @"Changes", kUIButtonBarButtonTitle,
6914 @"0", kUIButtonBarButtonType,
6915 nil],
6916
6917 [NSDictionary dictionaryWithObjectsAndKeys:
6918 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6919 @"manage-up.png", kUIButtonBarButtonInfo,
6920 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
6921 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6922 self, kUIButtonBarButtonTarget,
6923 @"Manage", kUIButtonBarButtonTitle,
6924 @"0", kUIButtonBarButtonType,
6925 nil],
6926
6927 [NSDictionary dictionaryWithObjectsAndKeys:
6928 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6929 @"search-up.png", kUIButtonBarButtonInfo,
6930 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
6931 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6932 self, kUIButtonBarButtonTarget,
6933 @"Search", kUIButtonBarButtonTitle,
6934 @"0", kUIButtonBarButtonType,
6935 nil],
6936 nil];
6937
6938 buttonbar_ = [[UIToolbar alloc]
6939 initInView:overlay_
6940 withFrame:CGRectMake(
6941 0, screenrect.size.height - ButtonBarHeight_,
6942 screenrect.size.width, ButtonBarHeight_
6943 )
6944 withItemList:buttonitems
6945 ];
6946
6947 [buttonbar_ setDelegate:self];
6948 [buttonbar_ setBarStyle:1];
6949 [buttonbar_ setButtonBarTrackingMode:2];
6950
6951 int buttons[5] = {1, 2, 3, 4, 5};
6952 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6953 [buttonbar_ showButtonGroup:0 withDuration:0];
6954
6955 for (int i = 0; i != 5; ++i)
6956 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
6957 i * 64 + 2, 1, 60, ButtonBarHeight_
6958 )];
6959
6960 [buttonbar_ showSelectionForButton:1];
6961 [overlay_ addSubview:buttonbar_];
6962
6963 [UIKeyboard initImplementationNow];
6964 CGSize keysize = [UIKeyboard defaultSize];
6965 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
6966 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
6967 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
6968 [overlay_ addSubview:keyboard_];
6969
6970 if (!bootstrap_)
6971 [underlay_ addSubview:overlay_];
6972
6973 [self reloadData];
6974
6975 sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
6976 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
6977 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6978
6979 manage_ = (ManageView *) [[self
6980 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6981 withClass:[ManageView class]
6982 ] retain];
6983
6984 PrintTimes();
6985
6986 if (bootstrap_)
6987 [self bootstrap];
6988 else
6989 [self _setHomePage];
6990 }
6991
6992 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
6993 NSString *context([sheet context]);
6994
6995 if ([context isEqualToString:@"missing"])
6996 [sheet dismiss];
6997 else if ([context isEqualToString:@"fixhalf"]) {
6998 switch (button) {
6999 case 1:
7000 @synchronized (self) {
7001 for (Package *broken in broken_) {
7002 [broken remove];
7003
7004 NSString *id = [broken id];
7005 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7006 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7007 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7008 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7009 }
7010
7011 [self resolve];
7012 [self perform];
7013 }
7014 break;
7015
7016 case 2:
7017 [broken_ removeAllObjects];
7018 [self _loaded];
7019 break;
7020
7021 default:
7022 _assert(false);
7023 }
7024
7025 [sheet dismiss];
7026 } else if ([context isEqualToString:@"role"]) {
7027 switch (button) {
7028 case 1: Role_ = @"User"; break;
7029 case 2: Role_ = @"Hacker"; break;
7030 case 3: Role_ = @"Developer"; break;
7031
7032 default:
7033 Role_ = nil;
7034 _assert(false);
7035 }
7036
7037 bool reset = Settings_ != nil;
7038
7039 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7040 Role_, @"Role",
7041 nil];
7042
7043 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7044
7045 Changed_ = true;
7046
7047 if (reset)
7048 [self updateData];
7049 else
7050 [self finish];
7051
7052 [sheet dismiss];
7053 } else if ([context isEqualToString:@"upgrade"]) {
7054 switch (button) {
7055 case 1:
7056 @synchronized (self) {
7057 for (Package *essential in essential_)
7058 [essential install];
7059
7060 [self resolve];
7061 [self perform];
7062 }
7063 break;
7064
7065 case 2:
7066 [self distUpgrade];
7067 break;
7068
7069 case 3:
7070 Ignored_ = YES;
7071 break;
7072
7073 default:
7074 _assert(false);
7075 }
7076
7077 [sheet dismiss];
7078 }
7079 }
7080
7081 - (void) reorganize { _pooled
7082 system("/usr/libexec/cydia/free.sh");
7083 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
7084 }
7085
7086 - (void) applicationSuspend:(__GSEvent *)event {
7087 if (hud_ == nil && ![progress_ isRunning])
7088 [super applicationSuspend:event];
7089 }
7090
7091 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7092 if (hud_ == nil)
7093 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7094 }
7095
7096 - (void) _setSuspended:(BOOL)value {
7097 if (hud_ == nil)
7098 [super _setSuspended:value];
7099 }
7100
7101 - (UIProgressHUD *) addProgressHUD {
7102 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
7103 [window_ setUserInteractionEnabled:NO];
7104 [hud show:YES];
7105 [progress_ addSubview:hud];
7106 return hud;
7107 }
7108
7109 - (void) removeProgressHUD:(UIProgressHUD *)hud {
7110 [hud show:NO];
7111 [hud removeFromSuperview];
7112 [window_ setUserInteractionEnabled:YES];
7113 }
7114
7115 - (void) openMailToURL:(NSURL *)url {
7116 // XXX: this makes me sad
7117 #if 0
7118 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
7119 #else
7120 [UIApp openURL:url];// asPanel:YES];
7121 #endif
7122 }
7123
7124 - (void) clearFirstResponder {
7125 if (id responder = [window_ firstResponder])
7126 [responder resignFirstResponder];
7127 }
7128
7129 - (RVPage *) pageForPackage:(NSString *)name {
7130 if (Package *package = [database_ packageWithName:name]) {
7131 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7132 [view setPackage:package];
7133 return view;
7134 } else {
7135 UIActionSheet *sheet = [[[UIActionSheet alloc]
7136 initWithTitle:@"Cannot Locate Package"
7137 buttons:[NSArray arrayWithObjects:@"Close", nil]
7138 defaultButtonIndex:0
7139 delegate:self
7140 context:@"missing"
7141 ] autorelease];
7142
7143 [sheet setBodyText:[NSString stringWithFormat:
7144 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7145 , name]];
7146
7147 [sheet popupAlertAnimated:YES];
7148 return nil;
7149 }
7150 }
7151
7152 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7153 if (tag != NULL)
7154 tag = 0;
7155
7156 NSString *scheme([[url scheme] lowercaseString]);
7157 if (![scheme isEqualToString:@"cydia"])
7158 return nil;
7159 NSString *path([url absoluteString]);
7160 if ([path length] < 8)
7161 return nil;
7162 path = [path substringFromIndex:8];
7163 if (![path hasPrefix:@"/"])
7164 path = [@"/" stringByAppendingString:path];
7165
7166 if ([path isEqualToString:@"/add-source"])
7167 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7168 else if ([path isEqualToString:@"/storage"])
7169 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
7170 else if ([path isEqualToString:@"/sources"])
7171 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7172 else if ([path isEqualToString:@"/packages"])
7173 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7174 else if ([path hasPrefix:@"/url/"])
7175 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
7176 else if ([path hasPrefix:@"/launch/"])
7177 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
7178 else if ([path hasPrefix:@"/package-settings/"])
7179 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
7180 else if ([path hasPrefix:@"/package-signature/"])
7181 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
7182 else if ([path hasPrefix:@"/package/"])
7183 return [self pageForPackage:[path substringFromIndex:9]];
7184 else if ([path hasPrefix:@"/files/"]) {
7185 NSString *name = [path substringFromIndex:7];
7186
7187 if (Package *package = [database_ packageWithName:name]) {
7188 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7189 [files setPackage:package];
7190 return files;
7191 }
7192 }
7193
7194 return nil;
7195 }
7196
7197 - (void) applicationOpenURL:(NSURL *)url {
7198 [super applicationOpenURL:url];
7199 int tag;
7200 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7201 [self setPage:page];
7202 [buttonbar_ showSelectionForButton:tag];
7203 tag_ = tag;
7204 }
7205 }
7206
7207 - (void) applicationDidFinishLaunching:(id)unused {
7208 _trace();
7209 Font12_ = [[UIFont systemFontOfSize:12] retain];
7210 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7211 Font14_ = [[UIFont systemFontOfSize:14] retain];
7212 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7213 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7214
7215 _assert(pkgInitConfig(*_config));
7216 _assert(pkgInitSystem(*_config, _system));
7217
7218 tag_ = 1;
7219
7220 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7221 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7222
7223 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7224
7225 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7226 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7227
7228 [window_ orderFront:self];
7229 [window_ makeKey:self];
7230 [window_ setHidden:NO];
7231
7232 database_ = [Database sharedInstance];
7233 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7234 [database_ setDelegate:progress_];
7235 [window_ setContentView:progress_];
7236
7237 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7238 [progress_ setContentView:underlay_];
7239
7240 [progress_ resetView];
7241
7242 if (
7243 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
7244 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
7245 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7246 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7247 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
7248 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7249 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
7250 ) {
7251 [self setIdleTimerDisabled:YES];
7252
7253 hud_ = [[self addProgressHUD] retain];
7254 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
7255
7256 [self setStatusBarShowsProgress:YES];
7257
7258 [NSThread
7259 detachNewThreadSelector:@selector(reorganize)
7260 toTarget:self
7261 withObject:nil
7262 ];
7263 } else
7264 [self finish];
7265 }
7266
7267 - (void) showKeyboard:(BOOL)show {
7268 CGSize keysize = [UIKeyboard defaultSize];
7269 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7270 CGRect keyup = keydown;
7271 keyup.origin.y -= keysize.height;
7272
7273 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7274 [animation setSignificantRectFields:2];
7275
7276 if (show) {
7277 [animation setStartFrame:keydown];
7278 [animation setEndFrame:keyup];
7279 [keyboard_ activate];
7280 } else {
7281 [animation setStartFrame:keyup];
7282 [animation setEndFrame:keydown];
7283 [keyboard_ deactivate];
7284 }
7285
7286 [[UIAnimator sharedAnimator]
7287 addAnimations:[NSArray arrayWithObjects:animation, nil]
7288 withDuration:KeyboardTime_
7289 start:YES
7290 ];
7291 }
7292
7293 - (void) slideUp:(UIActionSheet *)alert {
7294 if (Advanced_)
7295 [alert presentSheetFromButtonBar:buttonbar_];
7296 else
7297 [alert presentSheetInView:overlay_];
7298 }
7299
7300 @end
7301
7302 void AddPreferences(NSString *plist) { _pooled
7303 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7304 _assert(settings != NULL);
7305 NSMutableArray *items = [settings objectForKey:@"items"];
7306
7307 bool cydia(false);
7308
7309 for (NSMutableDictionary *item in items) {
7310 NSString *label = [item objectForKey:@"label"];
7311 if (label != nil && [label isEqualToString:@"Cydia"]) {
7312 cydia = true;
7313 break;
7314 }
7315 }
7316
7317 if (!cydia) {
7318 for (size_t i(0); i != [items count]; ++i) {
7319 NSDictionary *item([items objectAtIndex:i]);
7320 NSString *label = [item objectForKey:@"label"];
7321 if (label != nil && [label isEqualToString:@"General"]) {
7322 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7323 @"CydiaSettings", @"bundle",
7324 @"PSLinkCell", @"cell",
7325 [NSNumber numberWithBool:YES], @"hasIcon",
7326 [NSNumber numberWithBool:YES], @"isController",
7327 @"Cydia", @"label",
7328 nil] atIndex:(i + 1)];
7329
7330 break;
7331 }
7332 }
7333
7334 _assert([settings writeToFile:plist atomically:YES] == YES);
7335 }
7336 }
7337
7338 /*IMP alloc_;
7339 id Alloc_(id self, SEL selector) {
7340 id object = alloc_(self, selector);
7341 lprintf("[%s]A-%p\n", self->isa->name, object);
7342 return object;
7343 }*/
7344
7345 /*IMP dealloc_;
7346 id Dealloc_(id self, SEL selector) {
7347 id object = dealloc_(self, selector);
7348 lprintf("[%s]D-%p\n", self->isa->name, object);
7349 return object;
7350 }*/
7351
7352 int main(int argc, char *argv[]) { _pooled
7353 _trace();
7354 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7355
7356 bool substrate(false);
7357
7358 if (argc != 0) {
7359 char **args(argv);
7360 int arge(1);
7361
7362 for (int argi(1); argi != argc; ++argi)
7363 if (strcmp(argv[argi], "--") == 0) {
7364 arge = argi;
7365 argv[argi] = argv[0];
7366 argv += argi;
7367 argc -= argi;
7368 break;
7369 }
7370
7371 for (int argi(1); argi != arge; ++argi)
7372 if (strcmp(args[argi], "--bootstrap") == 0)
7373 bootstrap_ = true;
7374 else if (strcmp(args[argi], "--substrate") == 0)
7375 substrate = true;
7376 else
7377 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7378 }
7379
7380 App_ = [[NSBundle mainBundle] bundlePath];
7381 Home_ = NSHomeDirectory();
7382 Locale_ = CFLocaleCopyCurrent();
7383
7384 {
7385 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7386 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7387 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7388 Sounds_Keyboard_ = [keyboard boolValue];
7389 }
7390
7391 setuid(0);
7392 setgid(0);
7393
7394 #if 1 /* XXX: this costs 1.4s of startup performance */
7395 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7396 _assert(errno == ENOENT);
7397 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7398 _assert(errno == ENOENT);
7399 #endif
7400
7401 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7402 alloc_ = alloc->method_imp;
7403 alloc->method_imp = (IMP) &Alloc_;*/
7404
7405 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7406 dealloc_ = dealloc->method_imp;
7407 dealloc->method_imp = (IMP) &Dealloc_;*/
7408
7409 size_t size;
7410
7411 int maxproc;
7412 size = sizeof(maxproc);
7413 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7414 perror("sysctlbyname(\"kern.maxproc\", ?)");
7415 else if (maxproc < 64) {
7416 maxproc = 64;
7417 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7418 perror("sysctlbyname(\"kern.maxproc\", #)");
7419 }
7420
7421 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7422 char *machine = new char[size];
7423 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7424 perror("sysctlbyname(\"hw.machine\", ?)");
7425 else
7426 Machine_ = machine;
7427
7428 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
7429
7430 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
7431 Build_ = [system objectForKey:@"ProductBuildVersion"];
7432 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
7433 Product_ = [info objectForKey:@"SafariProductVersion"];
7434 Safari_ = [info objectForKey:@"CFBundleVersion"];
7435 }
7436
7437 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7438 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
7439
7440 _trace();
7441 Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"];
7442 _trace();
7443
7444 if (Metadata_ == NULL)
7445 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
7446 else {
7447 Settings_ = [Metadata_ objectForKey:@"Settings"];
7448
7449 Packages_ = [Metadata_ objectForKey:@"Packages"];
7450 Sections_ = [Metadata_ objectForKey:@"Sections"];
7451 Sources_ = [Metadata_ objectForKey:@"Sources"];
7452 }
7453
7454 if (Settings_ != nil)
7455 Role_ = [Settings_ objectForKey:@"Role"];
7456
7457 if (Packages_ == nil) {
7458 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7459 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7460 }
7461
7462 if (Sections_ == nil) {
7463 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7464 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7465 }
7466
7467 if (Sources_ == nil) {
7468 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7469 [Metadata_ setObject:Sources_ forKey:@"Sources"];
7470 }
7471
7472 #if RecycleWebViews
7473 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
7474 #endif
7475
7476 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
7477 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
7478 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7479 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
7480
7481 if (access("/User", F_OK) != 0) {
7482 _trace();
7483 system("/usr/libexec/cydia/firmware.sh");
7484 _trace();
7485 }
7486
7487 _assert([[NSFileManager defaultManager]
7488 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7489 withIntermediateDirectories:YES
7490 attributes:nil
7491 error:NULL
7492 ]);
7493
7494 space_ = CGColorSpaceCreateDeviceRGB();
7495
7496 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
7497 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
7498 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
7499 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
7500 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7501 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7502 Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
7503 Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
7504 Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
7505 /*Purple_.Set(space_, 1.0, 0.3, 0.0, 1.0);
7506 Purplish_.Set(space_, 1.0, 0.6, 0.4, 1.0); ORANGE */
7507 /*Purple_.Set(space_, 1.0, 0.5, 0.0, 1.0);
7508 Purplish_.Set(space_, 1.0, 0.7, 0.2, 1.0); ORANGISH */
7509 /*Purple_.Set(space_, 0.5, 0.0, 0.7, 1.0);
7510 Purplish_.Set(space_, 0.7, 0.4, 0.8, 1.0); PURPLE */
7511
7512 CommercialColor_ = [UIColor colorWithRed:0.93f green:1.00f blue:0.88f alpha:1.00f];
7513
7514 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7515
7516 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7517
7518 UIApplicationUseLegacyEvents(YES);
7519 UIKeyboardDisableAutomaticAppearance();
7520
7521 _trace();
7522 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
7523
7524 CGColorSpaceRelease(space_);
7525 CFRelease(Locale_);
7526
7527 return value;
7528 }