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