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