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