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