├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── common ├── Makefile ├── SDFileType.h ├── SDFileType.mm ├── SDResources.h ├── SDResources.mm ├── SDUserSettings.h └── SDUserSettings.mm ├── extension ├── Downloader.xm ├── Makefile ├── NSURLDownload.h ├── SDConcurrentOperation.h ├── SDConcurrentOperation.mm ├── SDDirectoryListViewController.h ├── SDDirectoryListViewController.mm ├── SDDownloadActionSheet.h ├── SDDownloadActionSheet.mm ├── SDDownloadButtonItem.h ├── SDDownloadCell.h ├── SDDownloadCell.mm ├── SDDownloadListViewController.h ├── SDDownloadListViewController.mm ├── SDDownloadManager.h ├── SDDownloadManager.mm ├── SDDownloadModel.h ├── SDDownloadModel.mm ├── SDDownloadPromptViewController.h ├── SDDownloadPromptViewController.mm ├── SDDownloadRequest.h ├── SDDownloadRequest.mm ├── SDFileBrowserNavigationController.h ├── SDFileBrowserNavigationController.mm ├── SDMCommon.h ├── SDMVersioning.h ├── SDNavigationController.h ├── SDNavigationController.mm ├── SDSafariDownload.h ├── SDSafariDownload.mm ├── SDUtils.h ├── SDUtils.mm ├── UIWebElementAction.h ├── WebPolicyDelegate.h ├── _BrowserPanelHooks.xm ├── _CustomToolbar_Old.xm ├── _CustomToolbar_iOS5.xm ├── _Interaction_iOS3.xm ├── _Interaction_iOS5.xm └── _WebPolicyDelegateHooks.xm ├── layout ├── Applications │ └── MobileSafari.app │ │ ├── Download.png │ │ ├── Download@2x.png │ │ ├── DownloadSmall.png │ │ ├── DownloadSmall@2x.png │ │ └── DownloadT.png ├── DEBIAN │ ├── control │ └── postinst └── Library │ ├── Application Support │ └── Safari Downloader │ │ ├── FileTypes.plist │ │ ├── Images.bundle │ │ ├── DownloadButton.png │ │ ├── DownloadButton@2x.png │ │ ├── DownloadButtonSmall.png │ │ ├── DownloadButtonSmall@2x.png │ │ ├── Icons │ │ │ ├── Category-Archives.png │ │ │ ├── Category-Archives@2x.png │ │ │ ├── Category-Documents.png │ │ │ ├── Category-Documents@2x.png │ │ │ ├── Category-Images.png │ │ │ ├── Category-Images@2x.png │ │ │ ├── Category-Media.png │ │ │ ├── Category-Media@2x.png │ │ │ ├── Category-Miscellaneous.png │ │ │ ├── Category-Miscellaneous@2x.png │ │ │ ├── application-pdf.png │ │ │ ├── application-pdf@2x.png │ │ │ ├── application-x-bittorrent.png │ │ │ ├── application-x-bittorrent@2x.png │ │ │ ├── application-x-executable.png │ │ │ ├── application-x-executable@2x.png │ │ │ ├── application-xml.png │ │ │ ├── application-xml@2x.png │ │ │ ├── audio-x-generic.png │ │ │ ├── audio-x-generic@2x.png │ │ │ ├── disk-image.png │ │ │ ├── disk-image@2x.png │ │ │ ├── folder.png │ │ │ ├── folder@2x.png │ │ │ ├── font-x-generic.png │ │ │ ├── font-x-generic@2x.png │ │ │ ├── home.png │ │ │ ├── home@2x.png │ │ │ ├── image-x-generic.png │ │ │ ├── image-x-generic@2x.png │ │ │ ├── package-x-generic.png │ │ │ ├── package-x-generic@2x.png │ │ │ ├── text-plain.png │ │ │ ├── text-plain@2x.png │ │ │ ├── text-x-generic.png │ │ │ ├── text-x-generic@2x.png │ │ │ ├── text-x-script.png │ │ │ ├── text-x-script@2x.png │ │ │ ├── unknown.png │ │ │ ├── unknown@2x.png │ │ │ ├── video-x-generic.png │ │ │ ├── video-x-generic@2x.png │ │ │ ├── x-office-address-book.png │ │ │ ├── x-office-address-book@2x.png │ │ │ ├── x-office-calendar.png │ │ │ ├── x-office-calendar@2x.png │ │ │ ├── x-office-document.png │ │ │ ├── x-office-document@2x.png │ │ │ ├── x-office-drawing.png │ │ │ ├── x-office-drawing@2x.png │ │ │ ├── x-office-presentation.png │ │ │ ├── x-office-presentation@2x.png │ │ │ ├── x-office-spreadsheet.png │ │ │ └── x-office-spreadsheet@2x.png │ │ └── Info.plist │ │ ├── Info.plist │ │ └── en.lproj │ │ ├── FileTypes.strings │ │ └── Localizable.strings │ ├── MobileSubstrate │ └── DynamicLibraries │ │ └── Downloader.plist │ └── PreferenceLoader │ └── Preferences │ └── SafariDownload.plist ├── preferences ├── Makefile ├── Resources │ ├── AutoRetry.plist │ ├── FileClass.plist │ ├── FileType.plist │ ├── Info.plist │ ├── SafariDownloader.plist │ └── en.lproj │ │ └── Localizable.strings ├── SDSettings.h ├── SDSettings.mm └── common ├── release.h └── sbsettings ├── Makefile ├── layout └── DEBIAN │ └── control └── main.mm /.gitignore: -------------------------------------------------------------------------------- 1 | Safari 2 | WebUI 3 | UIKitExtra 4 | SandCastle 5 | obj 6 | extension/obj 7 | preferences/obj 8 | framework 9 | *.deb 10 | _ 11 | .theos 12 | 13 | extension/OLD 14 | uikit5 15 | uikit3 16 | safari3.1 17 | safari4.3 18 | safari5 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 - 2013, Dustin L. Howett, Youssef Francis, collectively Cocoanuts, LLC. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Safari Download Manager nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #export TARGET=simulator 2 | #export DEBUG=1 3 | include framework/makefiles/common.mk 4 | 5 | export ADDITIONAL_CFLAGS = -include $(THEOS_PROJECT_DIR)/release.h 6 | SUBPROJECTS = extension 7 | ifneq ($(THEOS_TARGET_NAME),iphone_simulator) 8 | SUBPROJECTS += preferences 9 | endif 10 | 11 | export RELEASE.CFLAGS = -DRELEASE 12 | 13 | include framework/makefiles/aggregate.mk 14 | 15 | ifeq ($(findstring RELEASE,$(THEOS_SCHEMA)),) 16 | PACKAGE_BUILDNAME = private 17 | endif 18 | ifneq ($(findstring RELEASE,$(THEOS_SCHEMA)),) 19 | override TARGET_STRIP_FLAGS = -u 20 | export TARGET_STRIP_FLAGS 21 | endif 22 | 23 | internal-stage:: 24 | -find _ -iname '*.plist' -print0 | xargs -0 plutil -convert binary1 25 | -find _ -type f -iname '*.png' -print0 | xargs -0 pincrush -i 26 | $(FAKEROOT) chown -R 0:80 _ 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Safari Download Manager 2 | 3 | This is the [official open-source release](http://blog.howett.net/2015/11/safari-download-manager-the-end-of-an-era/) of Safari Download Manager. 4 | 5 | [Safari Download Manager](http://howett.net/cydia/depict/sdm/) is a reasonably complex tweak that adds a desktop-class download manager to Safari.app on iPhoneOS 3.1 to iOS 6.x. 6 | 7 | If you learn something great from this code, or write anything cool using it, you 8 | should [send me an e-mail](mailto:dustin@howett.net)! I'd be happy to answer any 9 | questions you have as well, so long as you're not asking why it 10 | doesn't support iOS 7 and beyond ;). 11 | 12 | ## Contributors 13 | 14 | * Dustin L. Howett (DHowett) 15 | * francis (francis) 16 | * Nicolas Haunold (westbaer) 17 | 18 | ## Building SDM 19 | 20 | SDM was historically compiled using [Theos](http://iphonedevwiki.net/index.php/Theos). The Makefile expects for the `framework/` directory to contain an appropriate version of Theos; it'll probably not work exceedingly well with newer versions, though we certainly hope that it does. 21 | 22 | ## License 23 | 24 | Safari Download Manager is available under the 3-Clause BSD License. 25 | 26 | In short, you can do pretty much anything you want with it: bundle it in a closed-source project, modify it without releasing your changes, and so on. Your end of the bargain, however, is that you must include the license/copyright notice in binary and source redistributions, and that you can't use the Safari Download Manager name, "SDM", my name, francis's name, or the name of Cocoanuts, LLC to endorse or promote your project. 27 | 28 | Additionally, if you do release a derivative on Cydia, I ask that you choose 29 | another bundle identifier. `net.howett.safaridownloader` is forever retired. 30 | 31 | For more details, check out `LICENSE` in the root of this repository! 32 | 33 | ## Miscellany 34 | 35 | * SDM's icon isn't included in this release. 36 | * We used icons from the [Tango Desktop Project](http://tango.freedesktop.org/Tango_Icon_Library) for our filetype icons, and they are in the public domain. 37 | -------------------------------------------------------------------------------- /common/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | SUBPROJECT_NAME = common filetypes 3 | common_FILES = SDResources.mm SDFileType.mm SDUserSettings.mm 4 | filetypes_FILES = $(common_FILES) 5 | filetypes_CFLAGS = -DSDFILETYPE_MAP_CATEGORIES=1 -DSDFILETYPE_NO_CUSTOM=1 6 | include $(THEOS_MAKE_PATH)/subproject.mk 7 | -------------------------------------------------------------------------------- /common/SDFileType.h: -------------------------------------------------------------------------------- 1 | typedef enum { 2 | SDFileTypeActionView = 0, 3 | SDFileTypeActionDownload = 1 4 | } SDFileTypeAction; 5 | 6 | @interface SDFileType : NSObject { 7 | NSArray *_MIMETypes; 8 | NSArray *_extensions; 9 | NSString *_name; 10 | NSString *_genericType; 11 | NSString *_category; 12 | BOOL _forceExtensionUse; 13 | } 14 | @property (nonatomic, readonly, copy) NSArray *MIMETypes; 15 | @property (nonatomic, readonly, copy) NSArray *extensions; 16 | @property (nonatomic, readonly, retain) NSString *name; 17 | @property (nonatomic, readonly, retain) NSString *genericType; 18 | @property (nonatomic, readonly, retain) NSString *category; 19 | @property (nonatomic, readonly, assign) BOOL forceExtensionUse; 20 | @property (nonatomic, readonly) NSString *primaryMIMEType; 21 | @property (nonatomic, readonly, assign) SDFileTypeAction defaultAction; 22 | @property (nonatomic, readonly, assign) BOOL hidden; 23 | + (void)loadAllFileTypes; 24 | + (void)unloadAllFileTypes; 25 | #ifndef SDFILETYPE_NO_CUSTOM 26 | + (void)reloadCustomFileTypes; 27 | #endif 28 | + (SDFileType *)fileTypeForMIMEType:(NSString *)MIMEType; 29 | + (SDFileType *)fileTypeForExtension:(NSString *)extension; 30 | + (SDFileType *)fileTypeForExtension:(NSString *)extension orMIMEType:(NSString *)MIMEType; 31 | #if SDFILETYPE_MAP_CATEGORIES == 1 32 | + (NSDictionary *)allCategories; 33 | #endif 34 | - (id)initWithName:(NSString *)name dictionary:(NSDictionary *)dictionary; 35 | @end 36 | -------------------------------------------------------------------------------- /common/SDFileType.mm: -------------------------------------------------------------------------------- 1 | #import "SDFileType.h" 2 | #import "SDUserSettings.h" 3 | #import "SDResources.h" 4 | 5 | static NSMutableDictionary *_MIMEMapping; 6 | static NSMutableDictionary *_extensionMapping; 7 | #if SDFILETYPE_MAP_CATEGORIES == 1 8 | static NSMutableDictionary *_categoryMapping; 9 | #endif 10 | 11 | #ifndef SDFILETYPE_NO_CUSTOM 12 | static NSMutableDictionary *_customMIMEMapping; 13 | static NSMutableDictionary *_customExtensionMapping; 14 | 15 | @interface _SDCustomFileType: SDFileType 16 | @end 17 | @implementation _SDCustomFileType 18 | - (BOOL)forceExtensionUse { return YES; } 19 | @end 20 | #endif 21 | 22 | @interface SDFileType () 23 | @property (nonatomic, copy) NSArray *MIMETypes; 24 | @property (nonatomic, copy) NSArray *extensions; 25 | @property (nonatomic, retain) NSString *name; 26 | @property (nonatomic, retain) NSString *genericType; 27 | @property (nonatomic, retain) NSString *category; 28 | @property (nonatomic, assign) BOOL forceExtensionUse; 29 | @property (nonatomic, assign) SDFileTypeAction defaultAction; 30 | @property (nonatomic, assign) BOOL hidden; 31 | + (void)_registerFileType:(SDFileType *)fileType; 32 | @end 33 | 34 | @implementation SDFileType 35 | @synthesize MIMETypes = _MIMETypes, extensions = _extensions, name = _name, genericType = _genericType, category = _category, forceExtensionUse = _forceExtensionUse, defaultAction = _defaultAction, hidden = _hidden; 36 | 37 | + (void)loadAllFileTypes { 38 | _MIMEMapping = [[NSMutableDictionary alloc] init]; 39 | _extensionMapping = [[NSMutableDictionary alloc] init]; 40 | #if SDFILETYPE_MAP_CATEGORIES == 1 41 | _categoryMapping = [[NSMutableDictionary alloc] init]; 42 | #endif 43 | 44 | NSDictionary *fileTypeMaster = [NSDictionary dictionaryWithContentsOfFile:[[SDResources supportBundle] pathForResource:@"FileTypes" ofType:@"plist"]]; 45 | for(NSString *fileTypeName in [fileTypeMaster allKeys]) { 46 | SDFileType *fileType = [[self alloc] initWithName:SDLocalizedStringInTable(fileTypeName, @"FileTypes") dictionary:[fileTypeMaster objectForKey:fileTypeName]]; 47 | [self _registerFileType:fileType]; 48 | [fileType release]; 49 | } 50 | } 51 | 52 | #ifndef SDFILETYPE_NO_CUSTOM 53 | + (void)reloadCustomFileTypes { 54 | if(_customMIMEMapping) [_customMIMEMapping release]; 55 | if(_customExtensionMapping) [_customExtensionMapping release]; 56 | _customMIMEMapping = [[NSMutableDictionary alloc] init]; 57 | _customExtensionMapping = [[NSMutableDictionary alloc] init]; 58 | NSDictionary *fileTypeMaster = [[SDUserSettings sharedInstance] customFileTypes]; 59 | for(NSString *fileTypeName in fileTypeMaster) { 60 | SDFileType *fileType = [[_SDCustomFileType alloc] initWithName:fileTypeName dictionary:[fileTypeMaster objectForKey:fileTypeName]]; 61 | [self _registerFileType:fileType]; 62 | [fileType release]; 63 | } 64 | } 65 | #endif 66 | 67 | + (void)_registerFileType:(SDFileType *)fileType { 68 | NSMutableDictionary *mimeMap = _MIMEMapping; 69 | NSMutableDictionary *extMap = _extensionMapping; 70 | #ifndef SDFILETYPE_NO_CUSTOM 71 | if(__builtin_expect([fileType isKindOfClass:[_SDCustomFileType class]], 0)) { 72 | mimeMap = _customMIMEMapping; 73 | extMap = _customExtensionMapping; 74 | } 75 | #endif 76 | 77 | for(NSString *MIMEType in fileType.MIMETypes) { 78 | [mimeMap setObject:fileType forKey:MIMEType]; 79 | } 80 | for(NSString *extension in fileType.extensions) { 81 | [extMap setObject:fileType forKey:extension]; 82 | } 83 | #if SDFILETYPE_MAP_CATEGORIES == 1 84 | NSMutableArray *_categoryArray = (NSMutableArray *)[_categoryMapping objectForKey:fileType.category]; 85 | if(!_categoryArray) { 86 | _categoryArray = [NSMutableArray array]; 87 | [_categoryMapping setObject:_categoryArray forKey:fileType.category]; 88 | } 89 | [_categoryArray addObject:fileType]; 90 | #endif 91 | } 92 | 93 | + (void)unloadAllFileTypes { 94 | [_MIMEMapping release]; 95 | [_extensionMapping release]; 96 | #if SDFILETYPE_MAP_CATEGORIES == 1 97 | [_categoryMapping release]; 98 | #endif 99 | #ifndef SDFILETYPE_NO_CUSTOM 100 | [_customMIMEMapping release]; 101 | [_customExtensionMapping release]; 102 | #endif 103 | } 104 | 105 | #ifndef SDFILETYPE_NO_CUSTOM 106 | + (SDFileType *)fileTypeForMIMEType:(NSString *)MIMEType { 107 | return [_customMIMEMapping objectForKey:MIMEType] ?: [_MIMEMapping objectForKey:MIMEType]; 108 | } 109 | 110 | + (SDFileType *)fileTypeForExtension:(NSString *)extension { 111 | return [_customExtensionMapping objectForKey:extension] ?: [_extensionMapping objectForKey:extension]; 112 | } 113 | 114 | + (SDFileType *)fileTypeForExtension:(NSString *)extension orMIMEType:(NSString *)MIMEType { 115 | return ([_customExtensionMapping objectForKey:extension] ?: [_extensionMapping objectForKey:extension]) 116 | ?: ([_customMIMEMapping objectForKey:MIMEType] ?: [_MIMEMapping objectForKey:MIMEType]); 117 | } 118 | #else 119 | + (SDFileType *)fileTypeForMIMEType:(NSString *)MIMEType { 120 | return [_MIMEMapping objectForKey:MIMEType]; 121 | } 122 | 123 | + (SDFileType *)fileTypeForExtension:(NSString *)extension { 124 | return [_extensionMapping objectForKey:extension]; 125 | } 126 | 127 | + (SDFileType *)fileTypeForExtension:(NSString *)extension orMIMEType:(NSString *)MIMEType { 128 | return [_extensionMapping objectForKey:extension] ?: [_MIMEMapping objectForKey:MIMEType]; 129 | } 130 | #endif 131 | 132 | #if SDFILETYPE_MAP_CATEGORIES == 1 133 | + (NSDictionary *)allCategories { 134 | return _categoryMapping; 135 | } 136 | #endif 137 | 138 | - (id)initWithName:(NSString *)name dictionary:(NSDictionary *)dictionary { 139 | if((self = [super init]) != nil) { 140 | self.name = name; 141 | self.MIMETypes = [dictionary objectForKey:@"Mimetypes"]; 142 | self.extensions = [dictionary objectForKey:@"Extensions"]; 143 | self.genericType = [dictionary objectForKey:@"GenericType"]; 144 | self.category = [dictionary objectForKey:@"Category"]; 145 | self.forceExtensionUse = [[dictionary objectForKey:@"ForceExtension"] boolValue]; 146 | self.defaultAction = (SDFileTypeAction)[[dictionary objectForKey:@"DefaultAction"] intValue]; 147 | self.hidden = [[dictionary objectForKey:@"Hidden"] boolValue]; 148 | } return self; 149 | } 150 | 151 | - (void)dealloc { 152 | [_MIMETypes release]; 153 | [_extensions release]; 154 | [_name release]; 155 | [_genericType release]; 156 | [_category release]; 157 | [super dealloc]; 158 | } 159 | 160 | - (NSString *)primaryMIMEType { 161 | return _MIMETypes.count == 0 ? nil : [_MIMETypes objectAtIndex:0]; 162 | } 163 | @end 164 | -------------------------------------------------------------------------------- /common/SDResources.h: -------------------------------------------------------------------------------- 1 | #define SDLocalizedString(key) NSLocalizedStringWithDefaultValue((key), nil, [SDResources supportBundle], (key), @"") 2 | #define SDLocalizedStringInTable(key, table) NSLocalizedStringWithDefaultValue((key), table, [SDResources supportBundle], (key), @"") 3 | @class SDFileType; 4 | @interface SDResources : NSObject 5 | + (NSBundle *)supportBundle; 6 | + (NSBundle *)imageBundle; 7 | + (UIImage *)imageNamed:(NSString *)name; 8 | + (UIImage *)iconForFolder; 9 | + (UIImage *)iconForFileType:(SDFileType *)fileType; 10 | @end 11 | -------------------------------------------------------------------------------- /common/SDResources.mm: -------------------------------------------------------------------------------- 1 | #import "SDResources.h" 2 | #import "SDFileType.h" 3 | 4 | #define SUPPORT_BUNDLE_PATH @"/Library/Application Support/Safari Downloader" 5 | 6 | @interface UIImage (iPhonePrivate) 7 | + (UIImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle; 8 | @end 9 | 10 | static NSBundle *_supportBundle; 11 | static NSBundle *_imageBundle; 12 | 13 | @implementation SDResources 14 | 15 | + (void)initialize { 16 | _supportBundle = [[NSBundle alloc] initWithPath:SUPPORT_BUNDLE_PATH]; 17 | _imageBundle = [[NSBundle alloc] initWithPath:[_supportBundle pathForResource:@"Images" ofType:@"bundle"]]; 18 | } 19 | 20 | + (NSBundle *)supportBundle { return _supportBundle; } 21 | + (NSBundle *)imageBundle { return _imageBundle; } 22 | 23 | + (UIImage *)imageNamed:(NSString *)name { 24 | return [UIImage imageNamed:name inBundle:_imageBundle]; 25 | } 26 | 27 | + (UIImage *)iconForFolder { 28 | return [self imageNamed:@"Icons/folder.png"]; 29 | } 30 | 31 | + (UIImage *)iconForFileType:(SDFileType *)fileType { 32 | NSString *primaryMIME = [fileType.primaryMIMEType stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; 33 | UIImage *icon = primaryMIME ? [self imageNamed:[NSString stringWithFormat:@"Icons/%@.png", primaryMIME]] : nil; 34 | if(!icon) { 35 | NSString *genericType = [fileType.genericType stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; 36 | icon = [self imageNamed:[NSString stringWithFormat:@"Icons/%@.png", genericType]]; 37 | } 38 | if(!icon) { 39 | icon = [self imageNamed:@"Icons/unknown.png"]; 40 | } 41 | return icon; 42 | 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /common/SDUserSettings.h: -------------------------------------------------------------------------------- 1 | extern const NSString * const kSDUserSettingsReloadedNotification; 2 | @interface SDUserSettings : NSObject { 3 | NSMutableDictionary *_settings; 4 | } 5 | + (id)sharedInstance; 6 | - (BOOL)boolForKey:(NSString *)key default:(BOOL)defaultValue; 7 | - (NSInteger)integerForKey:(NSString *)key default:(NSInteger)defaultValue; 8 | - (NSObject *)objectForKey:(NSObject *)key default:(NSObject *)defaultValue; 9 | - (float)floatForKey:(NSString *)key default:(float)defaultValue; 10 | - (NSArray *)arrayForKey:(NSString *)key; 11 | 12 | - (void)setObject:(NSObject *)object forKey:(NSString *)key; 13 | 14 | - (void)reloadSettings; 15 | - (void)commit; 16 | - (NSArray *)disabledItemNames; 17 | - (NSDictionary *)customFileTypes; 18 | @end 19 | -------------------------------------------------------------------------------- /common/SDUserSettings.mm: -------------------------------------------------------------------------------- 1 | #import "../extension/SDMCommon.h" 2 | #import "SDUserSettings.h" 3 | 4 | const NSString * const kSDUserSettingsReloadedNotification = @"kSDUserSettingsReloadedNotification"; 5 | 6 | @implementation SDUserSettings 7 | static SDUserSettings *_sharedSettings; 8 | + (id)sharedInstance { 9 | return _sharedSettings ?: _sharedSettings = [[self alloc] init]; 10 | } 11 | 12 | static NSString *_preferencesPath; 13 | + (NSString *)preferencesPath { 14 | return _preferencesPath ?: _preferencesPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/net.howett.safaridownloader.plist"] retain]; 15 | } 16 | 17 | - (id)copyWithZone:(NSZone *)zone { return self; } 18 | - (id)retain { return self; } 19 | - (unsigned)retainCount { return UINT_MAX; } 20 | - (void)release { } 21 | - (id)autorelease { return self; } 22 | 23 | - (BOOL)boolForKey:(NSString *)key default:(BOOL)defaultValue { 24 | NSNumber *b = [_settings objectForKey:key]; 25 | if(!b) return defaultValue; 26 | return [b boolValue]; 27 | } 28 | 29 | - (NSInteger)integerForKey:(NSString *)key default:(NSInteger)defaultValue { 30 | NSNumber *b = [_settings objectForKey:key]; 31 | if(!b) return defaultValue; 32 | return [b integerValue]; 33 | } 34 | 35 | - (NSObject *)objectForKey:(NSObject *)key default:(NSObject *)defaultValue { 36 | NSObject *b = [_settings objectForKey:key]; 37 | if(!b) return defaultValue; 38 | return b; 39 | } 40 | 41 | - (float)floatForKey:(NSString *)key default:(float)defaultValue { 42 | NSNumber *b = [_settings objectForKey:key]; 43 | if(!b) return defaultValue; 44 | return [b floatValue]; 45 | } 46 | 47 | - (NSArray *)arrayForKey:(NSString *)key { 48 | return [_settings objectForKey:key]; 49 | } 50 | 51 | - (void)setObject:(NSObject *)object forKey:(NSString *)key { 52 | [_settings setObject:object forKey:key]; 53 | } 54 | 55 | - (void)reloadSettings { 56 | if(_settings) [_settings release]; 57 | _settings = [([NSMutableDictionary dictionaryWithContentsOfFile:[[self class] preferencesPath]] ?: [NSMutableDictionary dictionary]) retain]; 58 | [[NSNotificationCenter defaultCenter] postNotificationName:kSDUserSettingsReloadedNotification object:self]; 59 | } 60 | 61 | - (void)commit { 62 | [_settings writeToFile:[[self class] preferencesPath] atomically:YES]; 63 | [self reloadSettings]; 64 | } 65 | 66 | - (NSArray *)disabledItemNames { 67 | return [_settings objectForKey:@"DisabledItems"]; 68 | } 69 | 70 | - (NSDictionary *)customFileTypes { 71 | return [_settings objectForKey:@"CustomItems"]; 72 | } 73 | @end 74 | -------------------------------------------------------------------------------- /extension/Downloader.xm: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDMVersioning.h" 3 | #import "Safari/BrowserController.h" 4 | 5 | #import "SDDownloadManager.h" 6 | 7 | #import "SDFileType.h" 8 | #import "SDUserSettings.h" 9 | 10 | Class SDM$BrowserController; 11 | bool SDM$WildCat = false; 12 | 13 | /* {{{ Private and Additional Categories */ 14 | @interface UIActionSheet (SDMPrivate) 15 | - (id)buttons; 16 | @end 17 | 18 | @interface UIDevice (Wildcat) 19 | - (BOOL)isWildcat; 20 | @end 21 | 22 | @interface UIScreen (iOS4) 23 | - (CGFloat)scale; 24 | @end 25 | 26 | /* }}} */ 27 | 28 | void _reloadPreferences(void); 29 | 30 | #pragma mark General Hooks/*{{{*/ 31 | %hook Application 32 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 33 | [SDFileType loadAllFileTypes]; 34 | [SDDownloadManager sharedManager]; // Initialize the Download Manager. 35 | _reloadPreferences(); 36 | %orig; 37 | } 38 | 39 | - (void)applicationResume:(void *)event { 40 | %orig; 41 | _reloadPreferences(); 42 | } 43 | 44 | %end 45 | #pragma mark -/*}}}*/ 46 | 47 | /* 48 | %hook Application 49 | %group Backgrounding 50 | - (BOOL)_suspendForEventsOnly:(BOOL)x { 51 | return %orig; 52 | //return [[SDDownloadManager sharedManager] downloadsRunning] > 0 ? YES : %orig; 53 | } 54 | - (void)applicationWillSuspend { 55 | %orig;//if([[SDDownloadManager sharedManager] downloadsRunning] == 0) %orig; 56 | } 57 | %end 58 | 59 | - (void)applicationSuspend:(void *)event { 60 | %orig;//if([[SDDownloadManager sharedManager] downloadsRunning] == 0) %orig; 61 | return; 62 | } 63 | 64 | - (void)applicationSuspend:(void *)event settings:(id)settings { 65 | %orig;//if([[SDDownloadManager sharedManager] downloadsRunning] == 0) %orig; 66 | return; 67 | } 68 | %end 69 | */ 70 | 71 | %group OldAuthenticationHooks 72 | %hook AuthenticationView 73 | - (void)setChallenge:(NSURLAuthenticationChallenge *)challenge { 74 | NSURLAuthenticationChallenge *overrideChallenge = objc_getAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedOverrideAuthenticationChallenge); 75 | if(overrideChallenge) 76 | challenge = overrideChallenge; 77 | 78 | %orig(); 79 | } 80 | %end 81 | 82 | %hook BrowserController 83 | - (void)logInFromAuthenticationView:(id)authenticationView withCredential:(NSURLCredential *)credential { 84 | NSURLAuthenticationChallenge *overrideChallenge = objc_getAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedOverrideAuthenticationChallenge); 85 | if(!overrideChallenge) { 86 | %orig; 87 | return; 88 | } 89 | 90 | [[overrideChallenge sender] useCredential:credential forAuthenticationChallenge:overrideChallenge]; 91 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedOverrideAuthenticationChallenge, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 92 | [self hideBrowserPanelType:5]; 93 | } 94 | 95 | - (void)cancelFromAuthenticationView:(id)authenticationView { 96 | NSURLAuthenticationChallenge *overrideChallenge = objc_getAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedOverrideAuthenticationChallenge); 97 | if(!overrideChallenge) { 98 | %orig; 99 | return; 100 | } 101 | 102 | [[overrideChallenge sender] cancelAuthenticationChallenge:overrideChallenge]; 103 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedOverrideAuthenticationChallenge, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 104 | [self _setShowingCurrentPanel:NO]; 105 | } 106 | %end 107 | %end 108 | 109 | void _reloadPreferences() { 110 | [[SDUserSettings sharedInstance] reloadSettings]; 111 | } 112 | 113 | static void ReloadPrefsNotification (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { 114 | _reloadPreferences(); 115 | } 116 | 117 | // Imported from the other Hook modules. 118 | void _init_customToolbar_legacy(void); 119 | void _init_customToolbar(void); 120 | void _init_browserPanel(void); 121 | void _init_interaction_legacy(void); 122 | void _init_interaction(void); 123 | void _init_webPolicyDelegate(void); 124 | 125 | %ctor { 126 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 127 | 128 | %init; 129 | //%init(Backgrounding); 130 | if(%c(AuthenticationView) != nil) { 131 | %init(OldAuthenticationHooks); 132 | } 133 | 134 | SDM$BrowserController = %c(BrowserController); 135 | if([UIDevice instancesRespondToSelector:@selector(isWildcat)] && [[UIDevice currentDevice] isWildcat]) { 136 | SDM$WildCat = true; 137 | } 138 | 139 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) { 140 | _init_customToolbar_legacy(); 141 | _init_interaction_legacy(); 142 | } else { 143 | _init_customToolbar(); 144 | _init_interaction(); 145 | } 146 | _init_browserPanel(); 147 | _init_webPolicyDelegate(); 148 | 149 | CFNotificationCenterRef r = CFNotificationCenterGetDarwinNotifyCenter(); 150 | CFNotificationCenterAddObserver(r, NULL, &ReloadPrefsNotification, CFSTR("net.howett.safaridownloader/ReloadPrefs"), NULL, 0); 151 | 152 | [pool drain]; 153 | } 154 | 155 | // vim:filetype=logos:sw=8:ts=8:noet 156 | -------------------------------------------------------------------------------- /extension/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(MAKELEVEL),0) 2 | all package install clean sim:: 3 | @cd ..; make $@ 4 | else 5 | 6 | 7 | TWEAK_NAME = Downloader 8 | Downloader_FILES = SDDownloadCell.mm 9 | Downloader_FILES += SDConcurrentOperation.mm SDSafariDownload.mm 10 | Downloader_FILES += SDUtils.mm 11 | 12 | Downloader_FILES += Downloader.xm _WebPolicyDelegateHooks.xm _BrowserPanelHooks.xm 13 | Downloader_FILES += _CustomToolbar_Old.xm _Interaction_iOS3.xm 14 | Downloader_FILES += _CustomToolbar_iOS5.xm _Interaction_iOS5.xm 15 | 16 | Downloader_FILES += SDDownloadManager.mm SDDownloadActionSheet.mm 17 | Downloader_FILES += SDDownloadModel.mm 18 | Downloader_FILES += SDNavigationController.mm 19 | Downloader_FILES += SDDownloadListViewController.mm 20 | Downloader_FILES += SDDownloadPromptViewController.mm 21 | Downloader_FILES += SDDirectoryListViewController.mm 22 | Downloader_FILES += SDFileBrowserNavigationController.mm 23 | Downloader_FILES += SDDownloadRequest.mm 24 | Downloader_FRAMEWORKS = UIKit CoreGraphics QuartzCore 25 | Downloader_PRIVATE_FRAMEWORKS = WebUI WebKit 26 | Downloader_LIBRARIES = SandCastle 27 | Downloader_CFLAGS = -I$(THEOS_PROJECT_DIR) -I$(THEOS_PROJECT_DIR)/common -mthumb 28 | Downloader_SUBPROJECTS = ../common:common 29 | Downloader_LDFLAGS = -mthumb -weak-lSystem 30 | 31 | include ../framework/makefiles/common.mk 32 | include ../framework/makefiles/tweak.mk 33 | endif 34 | -------------------------------------------------------------------------------- /extension/NSURLDownload.h: -------------------------------------------------------------------------------- 1 | /* 2 | NSURLDownload.h 3 | Copyright (C) 2003-2007, Apple Inc. All rights reserved. 4 | 5 | Public header file. 6 | */ 7 | 8 | // Note: To use the APIs described in these headers, you must perform 9 | // a runtime check for Foundation-462.1 or later. 10 | #import 11 | #if defined(MAC_OS_X_VERSION_10_2) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2) 12 | 13 | #import 14 | 15 | @class NSError; 16 | @class NSString; 17 | @class NSData; 18 | @class NSURLAuthenticationChallenge; 19 | @class NSURLDownloadInternal; 20 | @class NSURLRequest; 21 | @class NSURLResponse; 22 | 23 | /*! 24 | @class NSURLDownload 25 | @discussion A NSURLDownload loads a request and saves the downloaded data to a file. The progress of the download 26 | is reported via the NSURLDownloadDelegate protocol. Note: The word "download" is used to refer to the process 27 | of loading data off a network, decoding the data if necessary and saving the data to a file. 28 | */ 29 | @interface NSURLDownload : NSObject 30 | { 31 | @private 32 | NSURLDownloadInternal *_internal; 33 | } 34 | 35 | /*! 36 | @method canResumeDownloadDecodedWithEncodingMIMEType: 37 | @abstract Returns whether or not NSURLDownload can resume a download that was decoded with a given encoding MIME type. 38 | @param MIMEType The encoding MIME type. 39 | @description canResumeDownloadDecodedWithEncodingMIMEType: returns whether or not NSURLDownload can resume a download 40 | that was decoded with a given encoding MIME type. NSURLDownload cannot resume a download that was partially decoded 41 | in the gzip format for example. In order to ensure that a download can be later resumed, 42 | canResumeDownloadDecodedWithEncodingMIMEType: should be used when download:shouldDecodeSourceDataOfMIMEType: is called. 43 | */ 44 | + (BOOL)canResumeDownloadDecodedWithEncodingMIMEType:(NSString *)MIMEType; 45 | 46 | /*! 47 | @method initWithRequest:delegate: 48 | @abstract Initializes a NSURLDownload object and starts the download. 49 | @param request The request to download. Must not be nil. 50 | @param delegate The delegate of the download. 51 | @result An initialized NSURLDownload object. 52 | */ 53 | - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate; 54 | 55 | /*! 56 | @method initWithResumeData:delegate:path: 57 | @abstract Initializes a NSURLDownload object for resuming a previous download. 58 | @param resumeData The resume data from the previous download. 59 | @param delegate The delegate of the download. 60 | @param path The path of the incomplete downloaded file. 61 | @result An initialized NSURLDownload object. 62 | */ 63 | - (id)initWithResumeData:(NSData *)resumeData delegate:(id)delegate path:(NSString *)path; 64 | 65 | /*! 66 | @method cancel 67 | @abstract Cancels the download and deletes the downloaded file. 68 | */ 69 | - (void)cancel; 70 | 71 | /*! 72 | @method setDestination:allowOverwrite: 73 | @abstract Sets the destination path of the downloaded file. 74 | @param path The destination path of the downloaded file. 75 | @param allowOverwrite Allows a file of the same path to be overwritten. 76 | @discussion This method can be called after the download is created or in response to the 77 | decideDestinationWithSuggestedFilename: delegate method. It should only be called once. 78 | If NO is passed for allowOverwrite and a file of the same path exists, a number will be 79 | appended to the filename to prevent the overwrite. Because of this, use the path 80 | passed with didCreateDestination: to determine the actual path of the downloaded file. 81 | */ 82 | - (void)setDestination:(NSString *)path allowOverwrite:(BOOL)allowOverwrite; 83 | 84 | /*! 85 | @method request 86 | @abstract Returns the request of the download. 87 | @result The request of the download. 88 | */ 89 | - (NSURLRequest *)request; 90 | 91 | /*! 92 | @method resumeData 93 | @abstract Returns the resume data of a download that is incomplete. 94 | @result The resume data. 95 | @description resumeData returns the resume data of a download that is incomplete. This data represents the necessary 96 | state information that NSURLDownload needs to resume a download. The resume data can later be used when initializing 97 | a download with initWithResumeData:delegate:path:. Non-nil is returned if resuming the download seems possible. 98 | Non-nil is returned if the download was cancelled or ended in error after some but not all data has been received. 99 | The protocol of the download as well as the server must support resuming for non-nil to be returned. 100 | In order to later resume a download, be sure to call setDeletesFileUponFailure: with NO. 101 | */ 102 | - (NSData *)resumeData; 103 | 104 | /*! 105 | @method setDeletesFileUponFailure: 106 | @abstract Sets whether or not the downloaded file should be deleted upon failure. 107 | @param deletesFileUponFailure The value of deletesFileUponFailure. 108 | @description To allow the download to be resumed in case the download ends prematurely, 109 | deletesFileUponFailure must be set to NO as soon as possible to prevent the downloaded file 110 | from being deleted. 111 | */ 112 | - (void)setDeletesFileUponFailure:(BOOL)deletesFileUponFailure; 113 | 114 | /*! 115 | @method deletesFileUponFailure 116 | @abstract Returns whether or not the downloaded file should be deleted upon failure. 117 | @result The value of deletesFileUponFailure. deletesFileUponFailure is YES by default. 118 | */ 119 | - (BOOL)deletesFileUponFailure; 120 | 121 | @end 122 | 123 | /*! 124 | @protocol NSURLDownloadDelegate 125 | @discussion The NSURLDownloadDelegate delegate is used to report the progress of the download. 126 | */ 127 | @protocol NSURLDownloadDelegate 128 | @optional 129 | 130 | /*! 131 | @method downloadDidBegin: 132 | @abstract This method is called immediately after the download has started. 133 | @param download The download that just started downloading. 134 | */ 135 | - (void)downloadDidBegin:(NSURLDownload *)download; 136 | 137 | /*! 138 | @method download:willSendRequest:redirectResponse: 139 | @abstract This method is called if the download must load another request because the previous 140 | request was redirected. 141 | @param download The download that will send the request. 142 | @param request The request that will be used to continue loading. 143 | @result The request to be used; either the request parameter or a replacement. If nil is returned, 144 | the download is cancelled. 145 | @discussion This method gives the delegate an opportunity to inspect the request 146 | that will be used to continue loading the request, and modify it if necessary. 147 | */ 148 | - (NSURLRequest *)download:(NSURLDownload *)download willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse; 149 | 150 | /*! 151 | @method download:didReceiveAuthenticationChallenge: 152 | @abstract Start authentication for a given challenge 153 | @param download The download that needs authentication. 154 | @param challenge The NSURLAuthenticationChallenge for which to start authentication. 155 | @discussion Call useCredential:forAuthenticationChallenge:, 156 | continueWithoutCredentialForAuthenticationChallenge: or cancel on 157 | the connection sender when done. 158 | */ 159 | - (void)download:(NSURLDownload *)download didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 160 | 161 | /*! 162 | @method download:didCancelAuthenticationChallenge: 163 | @abstract Cancel authentication for a given request 164 | @param download The download that's cancelling 165 | @param challenge The NSURLAuthenticationChallenge to cancel authentication for 166 | */ 167 | - (void)download:(NSURLDownload *)download didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 168 | 169 | /*! 170 | @method download:didReceiveResponse: 171 | @abstract This method is called when the download has received a response from the server. 172 | @param download The download that now has a NSURLResponse available for inspection. 173 | @param response The NSURLResponse object for the given download. 174 | @discussion In some rare cases, multiple responses may be received for a single download. 175 | This occurs with multipart/x-mixed-replace, or "server push". In this case, the client 176 | should assume that each new response resets progress so far for the resource back to 0, 177 | and should check the new response for the expected content length. 178 | */ 179 | - (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response; 180 | 181 | /*! 182 | @method download:willResumeWithResponse:fromByte: 183 | @abstract This method is called when the download has received a response from the server after attempting to 184 | resume a download. 185 | @param download The download that now has a NSURLResponse available for inspection. 186 | @param response The NSURLResponse object for the given download. 187 | @param startingByte The number of bytes from where the download will resume. 0 indicates that the download will 188 | restart from the beginning. 189 | @discussion download:willResumeWithResponse:fromByte: is called instead of download:didReceiveResponse: 190 | when a download is initialized with initWithResumeData:delegate:path:. 191 | */ 192 | - (void)download:(NSURLDownload *)download willResumeWithResponse:(NSURLResponse *)response fromByte:(long long)startingByte; 193 | 194 | /*! 195 | @method download:didReceiveDataOfLength: 196 | @abstract This method is called when the download has loaded data. 197 | @param download The download that has received data. 198 | @param length The length of the received data. 199 | @discussion This method will be called one or more times. 200 | */ 201 | - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(NSUInteger)length; 202 | 203 | /*! 204 | @method download:shouldDecodeSourceDataOfMIMEType: 205 | @abstract This method is called if the download detects that the downloading file is encoded. 206 | @param download The download that has detected that the downloading file is encoded. 207 | @param mimeType A MIME type expressing the encoding type. 208 | @result Return YES to decode the file, NO to not decode the file. 209 | @discussion An encoded file is encoded in MacBinary, BinHex or gzip format. This method may be 210 | called more than once if the file is encoded multiple times. This method is not called if the 211 | download is not encoded. 212 | */ 213 | - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType; 214 | 215 | /*! 216 | @method download:decideDestinationWithSuggestedFilename: 217 | @abstract This method is called when enough information has been loaded to decide a destination 218 | for the downloaded file. 219 | @param download The download that requests the download path. 220 | @param filename The suggested filename for deciding the path of the downloaded file. The filename is either 221 | derived from the last path component of the URL and the MIME type or if the download was encoded, 222 | it is the filename specified in the encoding. 223 | @discussion Once the delegate has decided a path, it should call setDestination:allowOverwrite:. 224 | The delegate may respond immediately or later. This method is not called if 225 | setDestination:allowOverwrite: has already been called. 226 | */ 227 | - (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename; 228 | 229 | /*! 230 | @method download:didCreateDestination: 231 | @abstract This method is called after the download creates the downloaded file. 232 | @param download The download that created the downloaded file. 233 | @param path The path of the downloaded file. 234 | */ 235 | - (void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path; 236 | 237 | /*! 238 | @method downloadDidFinish: 239 | @abstract This method is called when the download has finished downloading. 240 | @param download The download that has finished downloading. 241 | @discussion This method is called after all the data has been received and written to disk. 242 | This method or download:didFailWithError: will only be called once. 243 | */ 244 | - (void)downloadDidFinish:(NSURLDownload *)download; 245 | 246 | /*! 247 | @method download:didFailWithError: 248 | @abstract This method is called when the download has failed. 249 | @param download The download that ended in error. 250 | @param error The error caused the download to fail. 251 | @discussion This method is called when the download encounters a network or file I/O related error. 252 | This method or downloadDidFinish: will only be called once. 253 | */ 254 | - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error; 255 | 256 | @end 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /extension/SDConcurrentOperation.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SDConcurrentOperation : NSOperation { 4 | BOOL _executing; 5 | BOOL _finished; 6 | } 7 | @property (nonatomic, assign, getter = isExecuting) BOOL executing; 8 | @property (nonatomic, assign, getter = isFinished) BOOL finished; 9 | - (void)complete; 10 | @end 11 | -------------------------------------------------------------------------------- /extension/SDConcurrentOperation.mm: -------------------------------------------------------------------------------- 1 | #import "SDConcurrentOperation.h" 2 | 3 | @interface SDConcurrentOperation () 4 | - (void)main_; 5 | @end 6 | 7 | 8 | @implementation SDConcurrentOperation 9 | @synthesize executing = _executing, finished = _finished; 10 | - (BOOL)isConcurrent { return YES; } 11 | 12 | - (BOOL)isExecuting { return _executing; } 13 | - (BOOL)isFinished { return _finished; } 14 | 15 | - (void)setExecuting:(BOOL)executing { 16 | [self willChangeValueForKey:@"isExecuting"]; 17 | _executing = executing; 18 | [self didChangeValueForKey:@"isExecuting"]; 19 | } 20 | 21 | - (void)setFinished:(BOOL)finished { 22 | [self willChangeValueForKey:@"isFinished"]; 23 | _finished = finished; 24 | [self didChangeValueForKey:@"isFinished"]; 25 | } 26 | 27 | - (void)start { 28 | if([self isCancelled]) { 29 | self.finished = YES; 30 | return; 31 | } 32 | self.executing = YES; 33 | [NSThread detachNewThreadSelector:@selector(main_) toTarget:self withObject:nil]; 34 | } 35 | 36 | - (void)main_ { 37 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 38 | [self main]; 39 | [pool drain]; 40 | } 41 | 42 | - (void)complete { 43 | self.executing = NO; 44 | self.finished = YES; 45 | } 46 | 47 | - (id)initWithCoder:(NSCoder *)aDecoder { 48 | self = [super init]; 49 | return self; 50 | } 51 | 52 | - (void)encodeWithCoder:(NSCoder *)aCoder { 53 | return; 54 | } 55 | @end 56 | -------------------------------------------------------------------------------- /extension/SDDirectoryListViewController.h: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | 3 | /*@protocol SDDownloadPrompt 4 | @property (nonatomic, retain) SDDownloadRequest *downloadRequest; 5 | @end 6 | 7 | @protocol SDDownloadPromptDelegate 8 | - (void)downloadPrompt:(NSObject *)downloadPrompt didCompleteWithAction:(SDActionType)action; 9 | @end 10 | */ 11 | 12 | @interface SDDirectoryListViewController : UITableViewController /**/ { 13 | NSString *_currentPath; 14 | NSArray *_fileList; 15 | } 16 | @property (nonatomic, copy) NSString *currentPath; 17 | - (id)initWithPath:(NSString *)path; 18 | - (void)reloadData; 19 | @end 20 | -------------------------------------------------------------------------------- /extension/SDDirectoryListViewController.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SDMCommon.h" 4 | #import "SDDirectoryListViewController.h" 5 | #import "SDDownloadManager.h" 6 | 7 | #import "SDResources.h" 8 | #import "SDFileType.h" 9 | 10 | #import "SDNavigationController.h" 11 | #import "SDFileBrowserNavigationController.h" 12 | 13 | #import "SandCastle/SandCastle.h" 14 | 15 | @interface UIAlertView (TextField) 16 | - (int)addTextFieldWithValue:(NSString *)value label:(NSString *)label; 17 | @end 18 | 19 | @interface _SDFileEntry: NSObject { 20 | BOOL _isDir; 21 | NSString *_path; 22 | NSString *_name; 23 | } 24 | @property (nonatomic, assign) BOOL isDir; 25 | @property (nonatomic, copy) NSString *path; 26 | @property (nonatomic, copy) NSString *name; 27 | + (_SDFileEntry *)fileEntryWithName:(NSString *)name atPath:(NSString *)path; 28 | - (id)initWithName:(NSString *)name atPath:(NSString *)path; 29 | @end 30 | 31 | @implementation _SDFileEntry 32 | @synthesize isDir = _isDir, name = _name, path = _path; 33 | + (_SDFileEntry *)fileEntryWithName:(NSString *)name atPath:(NSString *)path { 34 | _SDFileEntry *temp = [[self alloc] initWithName:name atPath:path]; 35 | return [temp autorelease]; 36 | } 37 | 38 | - (id)initWithName:(NSString *)name atPath:(NSString *)path { 39 | if((self = [super init]) != nil) { 40 | self.path = path; 41 | self.name = name; 42 | NSDictionary *attributes = [SandCastle attributesForItemAtPath:[path stringByAppendingPathComponent:name]]; 43 | self.isDir = [[attributes objectForKey:NSFileType] isEqualToString:NSFileTypeDirectory]; 44 | } return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [_name release]; 49 | [super dealloc]; 50 | } 51 | 52 | static NSComparisonResult _fileEntryComparator(id one, id two, void *context) { 53 | _SDFileEntry *o = (_SDFileEntry *)one; 54 | _SDFileEntry *t = (_SDFileEntry *)two; 55 | if(o.isDir && !t.isDir) { 56 | return NSOrderedAscending; 57 | } else if(t.isDir && !o.isDir) { 58 | return NSOrderedDescending; 59 | } else { 60 | return [o.name compare:t.name]; 61 | } 62 | } 63 | @end 64 | 65 | @implementation SDDirectoryListViewController 66 | @synthesize currentPath = _currentPath; 67 | - (NSString *)title { 68 | return [self.currentPath lastPathComponent]; 69 | } 70 | 71 | - (NSArray *)toolbarItems { 72 | return [(SDFileBrowserNavigationController *)[self navigationController] browserToolbarItems]; 73 | } 74 | 75 | - (id)initWithPath:(NSString *)path { 76 | if((self = [super initWithStyle:UITableViewStylePlain]) != nil) { 77 | self.currentPath = path; 78 | } return self; 79 | } 80 | 81 | - (void)dealloc { 82 | [_currentPath release]; 83 | [_fileList release]; 84 | [super dealloc]; 85 | } 86 | 87 | - (void)viewDidLoad { 88 | [super viewDidLoad]; 89 | [self reloadData]; 90 | 91 | self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(_newDirectoryButtonTapped:)] autorelease]; 92 | } 93 | 94 | - (void)reloadData { 95 | NSArray *fileList = [SandCastle directoryContentsAtPath:self.currentPath]; 96 | NSMutableArray *entries = [NSMutableArray arrayWithCapacity:fileList.count]; 97 | for(NSString *file in fileList) { 98 | if([file hasPrefix:@"."]) continue; 99 | [entries addObject:[_SDFileEntry fileEntryWithName:file atPath:_currentPath]]; 100 | } 101 | _fileList = [[entries sortedArrayUsingFunction:&_fileEntryComparator context:NULL] retain]; 102 | [self.tableView reloadData]; 103 | } 104 | 105 | - (void)viewWillUnload { 106 | [super viewWillUnload]; 107 | [_fileList release]; 108 | _fileList = nil; 109 | } 110 | 111 | - (int)numberOfSectionsInTableView:(UITableView *)tableView { 112 | return 1; 113 | } 114 | 115 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 116 | return nil; 117 | } 118 | 119 | - (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 120 | return _fileList.count; 121 | } 122 | 123 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 124 | NSString *reuseIdentifier = @"FileCell"; 125 | UITableViewCellStyle cellStyle = UITableViewCellStyleDefault; 126 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier] ?: [[[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:reuseIdentifier] autorelease]; 127 | 128 | _SDFileEntry *fileEntry = [_fileList objectAtIndex:indexPath.row]; 129 | NSString *filename = fileEntry.name; 130 | 131 | cell.textLabel.text = filename; 132 | if(fileEntry.isDir) { 133 | cell.imageView.image = [SDResources iconForFolder]; 134 | cell.selectionStyle = UITableViewCellSelectionStyleBlue; 135 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 136 | } else { 137 | SDFileType *fileType = [SDFileType fileTypeForExtension:[filename pathExtension] orMIMEType:nil]; 138 | cell.imageView.image = [SDResources iconForFileType:fileType]; 139 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 140 | cell.accessoryType = UITableViewCellAccessoryNone; 141 | } 142 | return cell; 143 | } 144 | 145 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 146 | _SDFileEntry *fileEntry = [_fileList objectAtIndex:indexPath.row]; 147 | if(fileEntry.isDir) { 148 | cell.textLabel.textColor = [UIColor blackColor]; 149 | } else { 150 | cell.textLabel.textColor = [UIColor lightGrayColor]; 151 | } 152 | 153 | } 154 | 155 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 156 | _SDFileEntry *fileEntry = [_fileList objectAtIndex:indexPath.row]; 157 | if(!fileEntry.isDir) return; 158 | SDDirectoryListViewController *newDirectoryViewController = [[[SDDirectoryListViewController alloc] initWithPath:[fileEntry.path stringByAppendingPathComponent:fileEntry.name]] autorelease]; 159 | [self.navigationController pushViewController:newDirectoryViewController animated:YES]; 160 | } 161 | 162 | - (void)_newDirectoryButtonTapped:(id)sender { 163 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:SDLocalizedString(@"NEW_DIRECTORY_TITLE") message:nil delegate:self cancelButtonTitle:SDLocalizedString(@"CANCEL") otherButtonTitles:SDLocalizedString(@"ACTION_CREATE_DIRECTORY"), nil]; 164 | [alertView addTextFieldWithValue:nil label:SDLocalizedString(@"NEW_DIRECTORY_NAME_PLACEHOLDER")]; 165 | [alertView show]; 166 | [alertView release]; 167 | } 168 | 169 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)index { 170 | if(index == alertView.cancelButtonIndex) return; 171 | NSString *directoryName = [[alertView textFieldAtIndex:0] text]; 172 | 173 | if(directoryName.length <= 0) return; 174 | NSString *newPath = [self.currentPath stringByAppendingPathComponent:directoryName]; 175 | [SandCastle createDirectoryAtResolvedPath:newPath]; 176 | [self reloadData]; 177 | 178 | SDDirectoryListViewController *newDirectoryViewController = [[[SDDirectoryListViewController alloc] initWithPath:newPath] autorelease]; 179 | [self.navigationController pushViewController:newDirectoryViewController animated:YES]; 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /extension/SDDownloadActionSheet.h: -------------------------------------------------------------------------------- 1 | #include "SDSafariDownload.h" 2 | 3 | extern const int kSDDownloadActionSheetTag; 4 | 5 | @class SDDownloadActionSheet; 6 | 7 | @protocol SDDownloadActionSheetDelegate 8 | - (void)downloadActionSheetWillDismiss:(SDDownloadActionSheet *)actionSheet; 9 | - (void)downloadActionSheet:(SDDownloadActionSheet *)actionSheet retryDownload:(SDSafariDownload *)download; 10 | - (void)downloadActionSheet:(SDDownloadActionSheet *)actionSheet deleteDownload:(SDSafariDownload *)download; 11 | @end 12 | 13 | @interface UIActionSheet (Private) 14 | - (id)buttonAtIndex:(int)index; 15 | @end 16 | 17 | @interface SDDownloadActionSheet: UIActionSheet { 18 | id _documentInteractionController; 19 | NSArray *_applications; 20 | SDSafariDownload *_download; 21 | id _sdDelegate; 22 | } 23 | @property(readonly) SDSafariDownload *download; 24 | - (id)initWithDownload:(SDSafariDownload *)download delegate:(id)delegate; 25 | @end 26 | -------------------------------------------------------------------------------- /extension/SDDownloadActionSheet.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SDDownloadActionSheet.h" 3 | #import 4 | #import "common/SDResources.h" 5 | 6 | #import 7 | 8 | const int kSDDownloadActionSheetTag = 903403; 9 | 10 | static NSMutableDictionary *_launchActions; 11 | 12 | @class LSApplicationProxy; 13 | @interface LSApplicationProxy : NSObject 14 | - (id)app; 15 | - (id)localizedName; 16 | @end 17 | 18 | @interface UIDocumentInteractionController (Private) 19 | - (NSArray *)_applications:(BOOL)unknown; 20 | - (void)_openDocumentWithApplication:(id)application; 21 | @end 22 | 23 | @implementation SDDownloadActionSheet 24 | @synthesize download = _download; 25 | 26 | + (void)initialize { 27 | _launchActions = [[NSMutableDictionary alloc] init]; 28 | if ([SandCastle fileExistsAtPath:@"/Applications/iFile.app"]) { 29 | [_launchActions setObject:@"ifile://" forKey:[NSString stringWithFormat:SDLocalizedString(@"OPEN_WITH_"), @"iFile"]]; 30 | } 31 | } 32 | 33 | - (id)initWithDownload:(SDSafariDownload *)download delegate:(id)delegate { 34 | self = [super initWithTitle:download.filename delegate:self cancelButtonTitle:nil destructiveButtonTitle:SDLocalizedString(@"DELETE") otherButtonTitles:nil]; 35 | if(self) { 36 | _sdDelegate = delegate; 37 | self.tag = kSDDownloadActionSheetTag; 38 | _download = download; 39 | if(_download.status == SDDownloadStatusFailed) [self addButtonWithTitle:SDLocalizedString(@"RETRY")]; 40 | else { 41 | _documentInteractionController = [[objc_getClass("UIDocumentInteractionController") interactionControllerWithURL: 42 | [NSURL fileURLWithPath:[_download.path stringByAppendingPathComponent:_download.filename]]] retain]; 43 | if(_documentInteractionController) { 44 | int buttonIndex = 0; 45 | _applications = [[_documentInteractionController _applications:YES] retain]; 46 | for(LSApplicationProxy *app in _applications) { 47 | UIButton *button = [self buttonAtIndex:[self addButtonWithTitle:[NSString stringWithFormat:SDLocalizedString(@"OPEN_WITH_"), [app localizedName]]]]; 48 | button.tag = -1 * (50+buttonIndex); 49 | NSLog(@"Added button with tag %d", button.tag); 50 | buttonIndex++; 51 | } 52 | } else { 53 | for(NSString *title in _launchActions) { 54 | [self buttonAtIndex:[self addButtonWithTitle:title]]; 55 | } 56 | } 57 | } 58 | self.cancelButtonIndex = [self addButtonWithTitle:SDLocalizedString(@"CANCEL")]; 59 | } 60 | return self; 61 | } 62 | 63 | - (void)dealloc { 64 | [_applications release]; 65 | [_documentInteractionController release]; 66 | [super dealloc]; 67 | } 68 | 69 | - (void)_IPCOpenWithAppID:(int)appId { 70 | [_documentInteractionController _openDocumentWithApplication:[_applications objectAtIndex:appId-1]]; 71 | } 72 | 73 | - (void)actionSheet:(SDDownloadActionSheet *)actionSheet clickedButtonAtIndex:(int)index { 74 | [_sdDelegate downloadActionSheetWillDismiss:self]; 75 | if(index == self.cancelButtonIndex) return; 76 | 77 | NSString *button = index >= 0 ? [self buttonTitleAtIndex:index] : nil; 78 | if([button isEqualToString:SDLocalizedString(@"DELETE")]) { 79 | [_sdDelegate downloadActionSheet:actionSheet deleteDownload:_download]; 80 | } else if([button isEqualToString:SDLocalizedString(@"RETRY")]) { 81 | [_sdDelegate downloadActionSheet:actionSheet retryDownload:_download]; 82 | } else { 83 | NSLog(@"button clicked with tag %d", index); 84 | if(index < 0) { 85 | [self _IPCOpenWithAppID:(-1*index)-50]; 86 | } else { 87 | NSString *action = [_launchActions objectForKey:button]; 88 | if(action) { 89 | Class Application = objc_getClass("Application"); 90 | NSString *path = [_download.path stringByAppendingPathComponent:_download.filename]; 91 | path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 92 | [[Application sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", action, path]]]; 93 | } 94 | } 95 | } 96 | } 97 | @end 98 | 99 | // vim:ft=objc 100 | -------------------------------------------------------------------------------- /extension/SDDownloadButtonItem.h: -------------------------------------------------------------------------------- 1 | @interface SpacedBarButtonItem: UIBarButtonItem 2 | - (void)destroyPrecedingSpace; 3 | @end 4 | 5 | @interface SDDownloadButtonItem: SpacedBarButtonItem 6 | - (void)setBadge:(NSString *)badge; 7 | - (NSString *)badge; 8 | @end 9 | -------------------------------------------------------------------------------- /extension/SDDownloadCell.h: -------------------------------------------------------------------------------- 1 | @class SDSafariDownload; 2 | @interface SDDownloadCell: UITableViewCell { 3 | SDSafariDownload *_download; 4 | UIProgressView *_progressView; 5 | UILabel *_nameLabel; 6 | UILabel *_statusLabel; 7 | UILabel *_sizeLabel; 8 | UILabel *_progressLabel; 9 | UIImageView *_iconImageView; 10 | } 11 | @property (nonatomic, retain) SDSafariDownload *download; 12 | - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier; 13 | - (void)updateDisplay; 14 | - (void)updateProgress; 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /extension/SDDownloadCell.mm: -------------------------------------------------------------------------------- 1 | #import "SDDownloadCell.h" 2 | #import "SDSafariDownload.h" 3 | #import "SDResources.h" 4 | #import "SDFileType.h" 5 | #import "SDUtils.h" 6 | 7 | @interface SDDownloadCell () 8 | - (void)_updateLabelColors; 9 | @end 10 | 11 | @implementation SDDownloadCell 12 | @synthesize download = _download; 13 | 14 | - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier { 15 | if((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) != nil) { 16 | _nameLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 17 | _nameLabel.font = [UIFont boldSystemFontOfSize:14.f]; 18 | 19 | _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 20 | _statusLabel.font = [UIFont boldSystemFontOfSize:13.f]; 21 | 22 | _sizeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 23 | _sizeLabel.font = [UIFont systemFontOfSize:12.f]; 24 | 25 | _progressLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 26 | _progressLabel.font = [UIFont systemFontOfSize:12.f]; 27 | 28 | _progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; 29 | _iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; 30 | 31 | [self.contentView addSubview:_nameLabel]; 32 | [self.contentView addSubview:_statusLabel]; 33 | [self.contentView addSubview:_sizeLabel]; 34 | [self.contentView addSubview:_progressLabel]; 35 | [self.contentView addSubview:_progressView]; 36 | [self.contentView addSubview:_iconImageView]; 37 | } return self; 38 | } 39 | 40 | - (void)dealloc { 41 | [_nameLabel release]; 42 | [_statusLabel release]; 43 | [_sizeLabel release]; 44 | [_progressLabel release]; 45 | [_progressView release]; 46 | [_iconImageView release]; 47 | [super dealloc]; 48 | } 49 | 50 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 51 | [super setSelected:selected animated:animated]; 52 | [self _updateLabelColors]; 53 | } 54 | 55 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { 56 | [super setHighlighted:highlighted animated:animated]; 57 | [self _updateLabelColors]; 58 | } 59 | 60 | - (void)_updateLabelColors { 61 | if((self.selected || self.highlighted) && self.selectionStyle != UITableViewCellSelectionStyleNone) { 62 | _nameLabel.textColor = [UIColor whiteColor]; 63 | _statusLabel.textColor = [UIColor whiteColor]; 64 | _sizeLabel.textColor = [UIColor whiteColor]; 65 | _progressLabel.textColor = [UIColor whiteColor]; 66 | } else { 67 | _nameLabel.textColor = [UIColor blackColor]; 68 | if(_download && (_download.status == SDDownloadStatusFailed || _download.status == SDDownloadStatusRetrying)) { 69 | _statusLabel.textColor = [UIColor colorWithRed:0.78f green:0.0f blue:0.0f alpha:1.0f]; 70 | } else { 71 | _statusLabel.textColor = [UIColor grayColor]; 72 | } 73 | _sizeLabel.textColor = [UIColor grayColor]; 74 | _progressLabel.textColor = [UIColor grayColor]; 75 | } 76 | } 77 | 78 | - (void)layoutSubviews { 79 | [super layoutSubviews]; 80 | [self _updateLabelColors]; 81 | 82 | CGRect bounds = CGRectInset(self.contentView.bounds, 6.f, 6.f); 83 | 84 | [_nameLabel sizeToFit]; 85 | [_statusLabel sizeToFit]; 86 | [_sizeLabel sizeToFit]; 87 | [_progressLabel sizeToFit]; 88 | 89 | CGSize iconSize = _iconImageView.image.size; 90 | CGRect iconFrame = (CGRect){bounds.origin, iconSize}; 91 | _iconImageView.frame = iconFrame; 92 | CGFloat bottomOfImage = bounds.origin.x + iconSize.height; 93 | CGSize filenameSize = _nameLabel.frame.size; 94 | CGPoint nameOffset = {CGRectGetMaxX(iconFrame) + 4, (bounds.origin.y + floorf((iconSize.height-filenameSize.height)/2.f))}; 95 | CGRect nameFrame = (CGRect){nameOffset, {bounds.size.width-(nameOffset.x-bounds.origin.x), filenameSize.height}}; 96 | CGFloat statusLineY = MAX(bottomOfImage, CGRectGetMaxY(nameFrame)) + 2.f; 97 | if(_download.status != SDDownloadStatusCompleted && _download.status != SDDownloadStatusFailed) { 98 | if(_download.totalBytes == ULLONG_MAX) { 99 | _progressLabel.hidden = YES; 100 | } else { 101 | _progressLabel.hidden = NO; 102 | } 103 | CGSize progressSize = _progressLabel.frame.size; 104 | _progressLabel.frame = (CGRect){{bounds.origin.y + bounds.size.width - progressSize.width, MAX(bottomOfImage, CGRectGetMaxY(nameFrame)) - progressSize.height}, progressSize}; 105 | nameFrame.size.width -= (progressSize.width + 2.f); 106 | 107 | _progressView.hidden = NO; 108 | _progressView.frame = (CGRect){{bounds.origin.x, statusLineY}, {bounds.size.width, _progressView.frame.size.height}}; 109 | statusLineY = CGRectGetMaxY(_progressView.frame) + 2.f; 110 | } else { 111 | _progressLabel.hidden = YES; 112 | _progressView.hidden = YES; 113 | } 114 | 115 | // We do this here so the progressLabel can take its chunk before we min on the filename size. 116 | nameFrame.size.width = MIN(nameFrame.size.width, filenameSize.width); 117 | _nameLabel.frame = nameFrame; 118 | 119 | CGSize sizeSize = CGSizeZero; 120 | if(_download.totalBytes != ULLONG_MAX) { 121 | _sizeLabel.hidden = NO; 122 | sizeSize = _sizeLabel.frame.size; 123 | _sizeLabel.frame = (CGRect){{CGRectGetMaxX(bounds) - sizeSize.width, statusLineY}, sizeSize}; 124 | } else { 125 | _sizeLabel.hidden = YES; 126 | } 127 | CGSize statusSize = _statusLabel.frame.size; 128 | _statusLabel.frame = (CGRect){{bounds.origin.y, statusLineY}, {MIN(statusSize.width, bounds.size.width - sizeSize.width - 4.f), statusSize.height}}; 129 | } 130 | 131 | - (void)setDownload:(SDSafariDownload *)download { 132 | [download retain]; 133 | [_download release]; 134 | _download = download; 135 | [self updateDisplay]; 136 | } 137 | 138 | - (void)updateDisplay { 139 | _nameLabel.text = _download.filename; 140 | _sizeLabel.text = [SDUtils formatSize:_download.totalBytes]; 141 | _iconImageView.image = [SDResources iconForFileType:[SDFileType fileTypeForExtension:[_download.filename pathExtension] orMIMEType:_download.mimeType]]; 142 | 143 | [self _updateLabelColors]; 144 | switch(_download.status) { 145 | case SDDownloadStatusRetrying: 146 | _statusLabel.text = SDLocalizedString(@"STATUS_RETRYING"); 147 | break; 148 | case SDDownloadStatusFailed: 149 | _statusLabel.text = SDLocalizedString(@"STATUS_FAILED"); 150 | break; 151 | case SDDownloadStatusCompleted: 152 | _statusLabel.text = [_download.path stringByAbbreviatingWithTildeInPath]; 153 | break; 154 | case SDDownloadStatusCancelled: 155 | _statusLabel.text = SDLocalizedString(@"STATUS_CANCELLED"); 156 | break; 157 | case SDDownloadStatusAuthenticationWaiting: 158 | _statusLabel.text = SDLocalizedString(@"STATUS_WAITING_AUTH"); 159 | break; 160 | case SDDownloadStatusRunning: 161 | break; 162 | default: 163 | _statusLabel.text = SDLocalizedString(@"STATUS_WAITING"); 164 | break; 165 | } 166 | 167 | [self updateProgress]; 168 | [self setNeedsLayout]; 169 | } 170 | 171 | - (void)updateProgress { 172 | float speed = ((double)_download.downloadedBytes - (double)_download.startedFromByte) / (-1*[_download.startDate timeIntervalSinceNow]); 173 | if(_download.totalBytes == 0 || _download.totalBytes == ULLONG_MAX) { 174 | _progressView.progress = 0.; 175 | } else { 176 | _progressView.progress = (double)_download.downloadedBytes / (double)_download.totalBytes; 177 | } 178 | _progressLabel.text = [NSString stringWithFormat:SDLocalizedString(@"PERCENTAGE"), (unsigned int)(_progressView.progress*100.f)]; 179 | if(_download.status == SDDownloadStatusRunning) { 180 | _statusLabel.text = [NSString stringWithFormat:SDLocalizedString(@"STATUS_DOWNLOADING_SPEED"), [SDUtils formatSize:speed]]; 181 | [self setNeedsLayout]; 182 | } 183 | } 184 | @end 185 | 186 | // vim:filetype=objc:ts=8:sw=8:noexpandtab 187 | -------------------------------------------------------------------------------- /extension/SDDownloadListViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SDDownloadListViewController 3 | * SDM 4 | * Dustin Howett 2012-01-30 5 | */ 6 | 7 | #import 8 | 9 | #import "SDDownloadActionSheet.h" 10 | #import "SDSafariDownload.h" 11 | 12 | @class SDDownloadModel; 13 | @protocol SDDownloadActionSheetDelegate; 14 | 15 | @interface SDDownloadListViewController : UITableViewController { 16 | NSIndexPath *_currentSelectedIndexPath; 17 | SDDownloadModel *_dataModel; 18 | NSTimer *_updateTimer; 19 | 20 | UIBarButtonItem *_doneButton; 21 | UIBarButtonItem *_cancelButton; 22 | UIBarButtonItem *_clearButton; 23 | } 24 | @property (nonatomic, retain) NSIndexPath *currentSelectedIndexPath; 25 | @end 26 | -------------------------------------------------------------------------------- /extension/SDDownloadListViewController.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * SDDownloadListViewController 3 | * SDM 4 | * Dustin Howett 2012-01-30 5 | */ 6 | #import 7 | #import "SDMCommon.h" 8 | #import "SDMVersioning.h" 9 | #import "SDDownloadManager.h" 10 | #import "SDDownloadListViewController.h" 11 | #import "SDDownloadCell.h" 12 | #import "SDResources.h" 13 | 14 | #import "SDFileType.h" 15 | 16 | #import "SDDownloadModel.h" 17 | 18 | @interface UIDevice (Wildcat) 19 | - (BOOL)isWildcat; 20 | @end 21 | 22 | @interface UIApplication (Safari) 23 | - (void)applicationOpenURL:(id)url; 24 | @end 25 | 26 | @interface SDDownloadListViewController () 27 | - (void)_updateRightButton; 28 | - (SDDownloadCell*)_cellForDownload:(SDSafariDownload*)download; 29 | @end 30 | @implementation SDDownloadListViewController 31 | @synthesize currentSelectedIndexPath = _currentSelectedIndexPath; 32 | 33 | #define kSDDownloadListViewControllerCancelAlertTag 1 34 | #define kSDDownloadListViewControllerClearAlertTag 2 35 | 36 | #pragma mark - 37 | 38 | - (int)panelType { 39 | return SDPanelTypeDownloadManager; 40 | } 41 | 42 | - (UIModalPresentationStyle)modalPresentationStyle { 43 | return UIModalPresentationFullScreen; 44 | } 45 | 46 | - (void)_attachToDownloadManager { 47 | if(!_dataModel) { 48 | _dataModel = [[SDDownloadManager sharedManager] dataModel]; 49 | [_dataModel addObserver:self forKeyPath:@"runningDownloads" options:0 context:NULL]; 50 | [_dataModel addObserver:self forKeyPath:@"finishedDownloads" options:0 context:NULL]; 51 | } 52 | [[SDDownloadManager sharedManager] setDownloadObserver:self]; 53 | } 54 | 55 | - (void)_detachFromDownloadManager { 56 | if(_dataModel) { 57 | [_dataModel removeObserver:self forKeyPath:@"finishedDownloads"]; 58 | [_dataModel removeObserver:self forKeyPath:@"runningDownloads"]; 59 | _dataModel = nil; 60 | } 61 | [[SDDownloadManager sharedManager] setDownloadObserver:nil]; 62 | } 63 | 64 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation { 65 | return SDM$WildCat ? YES : (orientation != UIInterfaceOrientationPortraitUpsideDown); 66 | } 67 | 68 | - (void)dealloc { 69 | [self _detachFromDownloadManager]; 70 | [_currentSelectedIndexPath release]; 71 | [_doneButton release]; 72 | [_cancelButton release]; 73 | [_clearButton release]; 74 | 75 | [super dealloc]; 76 | } 77 | 78 | - (void)cancelAllDownloads { 79 | UIAlertView* alert = nil; 80 | if(_dataModel.runningDownloads.count > 0) { 81 | alert = [[UIAlertView alloc] initWithTitle:SDLocalizedString(@"CANCEL_ALL_PROMPT") 82 | message:nil 83 | delegate:self 84 | cancelButtonTitle:SDLocalizedString(@"NO") 85 | otherButtonTitles:SDLocalizedString(@"YES"), nil]; 86 | alert.tag = kSDDownloadListViewControllerCancelAlertTag; 87 | } else { 88 | alert = [[UIAlertView alloc] initWithTitle:SDLocalizedString(@"NOTHING_TO_CANCEL") 89 | message:nil 90 | delegate:self 91 | cancelButtonTitle:SDLocalizedString(@"OK") 92 | otherButtonTitles:nil]; 93 | } 94 | 95 | [alert show]; 96 | [alert release]; 97 | } 98 | 99 | - (void)clearAllDownloads { 100 | UIAlertView *alert = nil; 101 | if(_dataModel.finishedDownloads.count > 0) { 102 | alert = [[UIAlertView alloc] initWithTitle:SDLocalizedString(@"CLEAR_ALL_PROMPT") 103 | message:nil 104 | delegate:self 105 | cancelButtonTitle:SDLocalizedString(@"NO") 106 | otherButtonTitles:SDLocalizedString(@"YES"), nil]; 107 | } else { 108 | alert = [[UIAlertView alloc] initWithTitle:SDLocalizedString(@"NOTHING_TO_CLEAR") 109 | message:nil 110 | delegate:self 111 | cancelButtonTitle:SDLocalizedString(@"OK") 112 | otherButtonTitles:nil]; 113 | } 114 | alert.tag = kSDDownloadListViewControllerClearAlertTag; 115 | [alert show]; 116 | [alert release]; 117 | } 118 | 119 | - (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex { 120 | if(alert.tag == kSDDownloadListViewControllerClearAlertTag) { 121 | if (buttonIndex == 1) { 122 | [_dataModel emptyList:SDDownloadModelFinishedList]; 123 | } 124 | } else { 125 | if (buttonIndex == 1) { 126 | [[SDDownloadManager sharedManager] cancelAllDownloads]; 127 | } 128 | } 129 | } 130 | 131 | - (SDDownloadCell*)_cellForDownload:(SDSafariDownload*)download { 132 | return (SDDownloadCell *)[self.tableView cellForRowAtIndexPath:[_dataModel indexPathForDownload:download]]; 133 | } 134 | 135 | #pragma mark -/*}}}*/ 136 | #pragma mark UIViewController Methods/*{{{*/ 137 | 138 | - (void)didReceiveMemoryWarning { 139 | [super didReceiveMemoryWarning]; 140 | } 141 | 142 | - (void)viewDidLoad { 143 | [super viewDidLoad]; 144 | 145 | _cancelButton = [[UIBarButtonItem alloc] initWithTitle:SDLocalizedString(@"CANCEL_ALL_SHORT") style:UIBarButtonItemStylePlain target:self action:@selector(cancelAllDownloads)]; 146 | _clearButton = [[UIBarButtonItem alloc] initWithTitle:SDLocalizedString(@"CLEAR_ALL_SHORT") style:UIBarButtonItemStylePlain target:self action:@selector(clearAllDownloads)]; 147 | 148 | if(!SDM$WildCat) { 149 | _doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone 150 | target:[self navigationController] 151 | action:@selector(close)]; 152 | self.navigationItem.leftBarButtonItem = _doneButton; 153 | self.navigationItem.leftBarButtonItem.enabled = YES; 154 | } 155 | 156 | self.tableView.rowHeight = 56; 157 | } 158 | 159 | - (void)viewDidUnload { 160 | [_doneButton release]; 161 | _doneButton = nil; 162 | [_cancelButton release]; 163 | _cancelButton = nil; 164 | [_clearButton release]; 165 | _clearButton = nil; 166 | 167 | [super viewDidUnload]; 168 | } 169 | 170 | - (void)updateRunningDownloads:(NSTimer *)timer { 171 | NSArray *indexPaths = [self.tableView indexPathsForVisibleRows]; 172 | for(NSIndexPath *indexPath in indexPaths) { 173 | if(indexPath.section != 0) continue; 174 | SDSafariDownload *download = [_dataModel.runningDownloads objectAtIndex:indexPath.row]; 175 | if(download.status != SDDownloadStatusRunning) continue; 176 | 177 | [(SDDownloadCell *)[self.tableView cellForRowAtIndexPath:indexPath] updateProgress]; 178 | } 179 | } 180 | 181 | - (void)viewWillAppear:(BOOL)animated { 182 | [self _attachToDownloadManager]; 183 | [self _updateRightButton]; 184 | [super viewWillAppear:animated]; 185 | [self.tableView reloadData]; 186 | _updateTimer = [[NSTimer scheduledTimerWithTimeInterval:.5f target:self selector:@selector(updateRunningDownloads:) userInfo:nil repeats:YES] retain]; 187 | } 188 | 189 | - (void)viewWillDisappear:(BOOL)animated { 190 | [_updateTimer invalidate]; 191 | [_updateTimer release]; 192 | _updateTimer = nil; 193 | [self _detachFromDownloadManager]; 194 | [super viewWillDisappear:animated]; 195 | } 196 | 197 | - (id)title { 198 | return SDLocalizedString(@"DOWNLOADS_TITLE"); 199 | } 200 | 201 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 202 | NSUInteger section; 203 | if([keyPath isEqualToString:@"runningDownloads"]) section = 0; 204 | else section = 1; 205 | NSIndexSet *indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; 206 | NSUInteger indices[indexSet.count]; 207 | [indexSet getIndexes:indices maxCount:indexSet.count inIndexRange:nil]; 208 | NSMutableArray *newIndexPaths = [NSMutableArray array]; 209 | for(unsigned int i = 0; i < indexSet.count; i++) { 210 | [newIndexPaths addObject:[NSIndexPath indexPathForRow:indices[i] inSection:section]]; 211 | } 212 | switch([[change objectForKey:NSKeyValueChangeKindKey] intValue]) { 213 | case NSKeyValueChangeInsertion: 214 | [self.tableView insertRowsAtIndexPaths:newIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 215 | break; 216 | case NSKeyValueChangeRemoval: 217 | [self.tableView deleteRowsAtIndexPaths:newIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 218 | break; 219 | } 220 | [self _updateRightButton]; 221 | } 222 | 223 | /* {{{ Download Observer */ 224 | - (void)downloadDidChangeStatus:(SDSafariDownload *)download { 225 | SDDownloadCell *cell = [self _cellForDownload:download]; 226 | if(!cell) return; 227 | [cell updateDisplay]; 228 | } 229 | 230 | - (void)downloadDidUpdateMetadata:(SDSafariDownload *)download { 231 | SDDownloadCell *cell = [self _cellForDownload:download]; 232 | if(!cell) return; 233 | [cell updateDisplay]; 234 | } 235 | /* }}} */ 236 | 237 | - (void)_updateRightButton { 238 | if(_dataModel.runningDownloads.count > 0) { 239 | [self.navigationItem setRightBarButtonItem:_cancelButton animated:YES]; 240 | } else { 241 | if(_dataModel.finishedDownloads.count > 0) { 242 | [self.navigationItem setRightBarButtonItem:_clearButton animated:YES]; 243 | } else { 244 | [self.navigationItem setRightBarButtonItem:nil animated:YES]; 245 | } 246 | } 247 | } 248 | 249 | #pragma mark -/*}}}*/ 250 | #pragma mark UITableView methods/*{{{*/ 251 | 252 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 253 | return 2; 254 | } 255 | 256 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 257 | return 0.f; 258 | } 259 | 260 | - (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 261 | return nil; 262 | } 263 | 264 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 265 | if (tableView.numberOfSections == 2 && section == 0) 266 | return _dataModel.runningDownloads.count; 267 | else 268 | return _dataModel.finishedDownloads.count; 269 | } 270 | 271 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 272 | static NSString *CellIdentifier = @"SDDownloadCell"; 273 | BOOL finished = NO; 274 | SDSafariDownload *download = nil; 275 | 276 | if(tableView.numberOfSections == 2 && indexPath.section == 0) { 277 | download = [_dataModel.runningDownloads objectAtIndex:indexPath.row]; 278 | finished = NO; 279 | } else { 280 | CellIdentifier = @"FinishedSDDownloadCell"; 281 | download = [_dataModel.finishedDownloads objectAtIndex:indexPath.row]; 282 | finished = YES; 283 | } 284 | 285 | SDDownloadCell *cell = (SDDownloadCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 286 | if (cell == nil) { 287 | cell = [[[SDDownloadCell alloc] initWithReuseIdentifier:CellIdentifier] autorelease]; 288 | } 289 | 290 | cell.download = download; 291 | 292 | if(!finished) { 293 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 294 | } else { 295 | cell.selectionStyle = UITableViewCellSelectionStyleBlue; 296 | } 297 | 298 | [cell updateDisplay]; 299 | return cell; 300 | } 301 | 302 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 303 | if(indexPath.section == 0) return 68; 304 | else return 56; 305 | } 306 | 307 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 308 | if(indexPath.section == 0) { 309 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 310 | return; 311 | } 312 | self.currentSelectedIndexPath = indexPath; 313 | if(indexPath.section == 1) { 314 | id download = [_dataModel.finishedDownloads objectAtIndex:indexPath.row]; 315 | id launch = [[SDDownloadActionSheet alloc] initWithDownload:download delegate:self]; 316 | [launch showInView:self.view]; 317 | [launch release]; 318 | } 319 | } 320 | 321 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 322 | return YES; 323 | } 324 | 325 | - (NSString *)tableView:(UITableView *)tableView 326 | titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { 327 | if(indexPath.section == 0) 328 | return SDLocalizedString(@"CANCEL"); 329 | else // local files 330 | return SDLocalizedString(@"CLEAR"); 331 | } 332 | 333 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 334 | if (editingStyle == UITableViewCellEditingStyleDelete) { 335 | if(indexPath.section == 0) { 336 | id download = [_dataModel.runningDownloads objectAtIndex:indexPath.row]; 337 | [[SDDownloadManager sharedManager] cancelDownload:download]; 338 | } else { 339 | [_dataModel removeDownload:[_dataModel.finishedDownloads objectAtIndex:indexPath.row] fromList:SDDownloadModelFinishedList]; 340 | } 341 | } 342 | } 343 | /*}}}*/ 344 | 345 | - (void)downloadActionSheet:(SDDownloadActionSheet *)actionSheet retryDownload:(SDSafariDownload *)download { 346 | [[SDDownloadManager sharedManager] retryDownload:download]; 347 | } 348 | 349 | - (void)downloadActionSheet:(SDDownloadActionSheet *)actionSheet deleteDownload:(SDSafariDownload *)download { 350 | [[SDDownloadManager sharedManager] deleteDownload:download]; 351 | } 352 | 353 | - (void)downloadActionSheetWillDismiss:(SDDownloadActionSheet *)actionSheet { 354 | [(UITableView *)self.view deselectRowAtIndexPath:self.currentSelectedIndexPath animated:YES]; 355 | self.currentSelectedIndexPath = nil; 356 | } 357 | @end 358 | 359 | // vim:filetype=objc:ts=8:sw=8:noexpandtab 360 | -------------------------------------------------------------------------------- /extension/SDDownloadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // DownloadManager.h 3 | // Downloader 4 | // 5 | // Created by Youssef Francis on 7/23/09. 6 | // Copyright 2009 Brancipater Software. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "Safari/BrowserButtonBar.h" 12 | #import "WebPolicyDelegate.h" 13 | #import "UIKitExtra/UIToolbarButton.h" 14 | #import "SDDownloadActionSheet.h" 15 | 16 | #import "SDDownloadPromptViewController.h" 17 | #import "SDFileBrowserNavigationController.h" 18 | 19 | @interface WebView : NSObject 20 | + (BOOL)canShowMIMEType:(NSString*)type; 21 | @end 22 | 23 | @class BrowserButtonBar; 24 | @class SDDownloadModel; 25 | 26 | @interface SDDownloadManager : UIViewController { 27 | NSOperationQueue *_downloadQueue; 28 | BOOL _visible; 29 | SDDownloadModel *_model; 30 | NSObject *_downloadObserver; 31 | id _authenticationManager; 32 | } 33 | @property (nonatomic, readonly, retain) SDDownloadModel *dataModel; 34 | @property (nonatomic, assign) NSObject *downloadObserver; 35 | @property (nonatomic, retain) WebUIAuthenticationManager *authenticationManager; 36 | 37 | + (id)uniqueFilenameForFilename:(NSString *)filename atPath:(NSString *)path; 38 | 39 | + (id)sharedManager; 40 | - (BOOL)supportedRequest:(NSURLRequest *)request 41 | withMimeType:(NSString *)mimeType; 42 | 43 | - (NSString*)fileNameForURL:(NSURL*)url; 44 | - (BOOL) webView:(WebView *)webView 45 | decideAction:(NSDictionary*)action 46 | forRequest:(NSURLRequest *)request 47 | withMimeType:(NSString *)mimeType 48 | inFrame:(WebFrame *)frame 49 | withListener:(id)listener 50 | context:(id)context; 51 | 52 | - (SDDownloadRequest *)downloadRequestForImmediateURLRequest:(NSURLRequest *)request context:(id)context; 53 | - (void)addDownloadFromDownloadRequest:(SDDownloadRequest *)downloadRequest; 54 | - (BOOL)cancelDownload:(SDSafariDownload *)download; 55 | - (void)retryDownload:(SDSafariDownload *)download; 56 | - (void)deleteDownload:(SDSafariDownload *)download; 57 | - (void)cancelAllDownloads; 58 | 59 | - (int)downloadsRunning; 60 | @end 61 | -------------------------------------------------------------------------------- /extension/SDDownloadModel.h: -------------------------------------------------------------------------------- 1 | typedef enum { 2 | SDDownloadModelRunningList = 0, 3 | SDDownloadModelFinishedList, 4 | } SDDownloadModelList; 5 | 6 | @class SDSafariDownload; 7 | 8 | @interface SDDownloadModel : NSObject { 9 | NSMutableArray *_runningDownloads; 10 | NSMutableArray *_finishedDownloads; 11 | } 12 | @property (nonatomic, retain, readonly) NSArray *runningDownloads; 13 | @property (nonatomic, retain, readonly) NSArray *finishedDownloads; 14 | - (id)init; 15 | - (void)loadData; 16 | - (void)saveData; 17 | - (SDSafariDownload *)downloadWithURL:(NSURL*)url; 18 | - (void)addDownload:(SDSafariDownload *)downloadi toList:(SDDownloadModelList)list; 19 | - (void)removeDownload:(SDSafariDownload *)download fromList:(SDDownloadModelList)list; 20 | - (void)emptyList:(SDDownloadModelList)list; 21 | - (void)moveDownload:(SDSafariDownload *)download toList:(SDDownloadModelList)list; 22 | - (NSIndexPath *)indexPathForDownload:(SDSafariDownload *)download; 23 | @end 24 | -------------------------------------------------------------------------------- /extension/SDDownloadModel.mm: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDDownloadModel.h" 3 | 4 | #import 5 | #import "SDSafariDownload.h" 6 | 7 | @interface SDDownloadModel () 8 | - (NSMutableArray *)_arrayForListType:(SDDownloadModelList)list keyName:(NSString **)keyNamePtr; 9 | @end 10 | 11 | @implementation SDDownloadModel 12 | @synthesize runningDownloads = _runningDownloads, finishedDownloads = _finishedDownloads; 13 | static NSString *_archivePath; 14 | + (NSString *)archivePath { 15 | if(_archivePath) return _archivePath; 16 | NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 17 | _archivePath = [[cachePath stringByAppendingPathComponent:@"net.howett.safaridownloader.plist"] retain]; 18 | return _archivePath; 19 | } 20 | 21 | - (id)init { 22 | if((self = [super init]) != nil) { 23 | _runningDownloads = [[NSMutableArray alloc] init]; 24 | _finishedDownloads = [[NSMutableArray alloc] init]; 25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveData) name:UIApplicationWillTerminateNotification object:nil]; 26 | } return self; 27 | } 28 | 29 | - (void)loadData { 30 | NSString *path = @"/tmp/.sdm.plist"; 31 | [SandCastle removeItemAtResolvedPath:path]; 32 | [SandCastle copyTemporaryFile:[[self class] archivePath] toResolvedPath:path]; 33 | NSDictionary *loaded = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 34 | if(loaded) { 35 | [_runningDownloads addObjectsFromArray:[loaded objectForKey:@"running"]]; 36 | [_finishedDownloads addObjectsFromArray:[loaded objectForKey:@"finished"]]; 37 | } 38 | NSLog(@"loaded %@", loaded); 39 | } 40 | 41 | - (void)saveData { 42 | NSString *path = @"/tmp/.sdm.plist"; 43 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject: 44 | [NSDictionary dictionaryWithObjectsAndKeys:_runningDownloads, @"running", 45 | _finishedDownloads, @"finished", nil]]; 46 | if(data) { 47 | [data writeToFile:path atomically:YES]; 48 | [SandCastle removeItemAtResolvedPath:[[self class] archivePath]]; 49 | [SandCastle copyTemporaryFile:path toResolvedPath:[[self class] archivePath]]; 50 | } 51 | } 52 | 53 | - (NSMutableArray *)_arrayForListType:(SDDownloadModelList)list keyName:(NSString **)keyNamePtr { 54 | switch(list) { 55 | case SDDownloadModelRunningList: 56 | if(keyNamePtr) *keyNamePtr = @"runningDownloads"; 57 | return _runningDownloads; 58 | case SDDownloadModelFinishedList: 59 | if(keyNamePtr) *keyNamePtr = @"finishedDownloads"; 60 | return _finishedDownloads; 61 | } 62 | return nil; 63 | } 64 | 65 | - (SDSafariDownload *)downloadWithURL:(NSURL*)url { 66 | for (SDSafariDownload *download in _runningDownloads) { 67 | if ([[download.URLRequest URL] isEqual:url]) 68 | return download; 69 | } 70 | return nil; 71 | } 72 | 73 | - (void)addDownload:(SDSafariDownload *)download toList:(SDDownloadModelList)list { 74 | NSString *keyName = nil; 75 | NSMutableArray *array = [self _arrayForListType:list keyName:&keyName]; 76 | NSLog(@"Adding download %@ to list %@ key %@", download, array, keyName); 77 | [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:0] forKey:keyName]; 78 | [array insertObject:download atIndex:0]; 79 | [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:0] forKey:keyName]; 80 | [self saveData]; 81 | } 82 | 83 | - (void)removeDownload:(SDSafariDownload *)download fromList:(SDDownloadModelList)list { 84 | NSString *keyName = nil; 85 | NSMutableArray *array = [self _arrayForListType:list keyName:&keyName]; 86 | NSInteger index = [array indexOfObjectIdenticalTo:download]; 87 | if(index == NSNotFound) return; 88 | [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:keyName]; 89 | [array removeObjectAtIndex:index]; 90 | [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:keyName]; 91 | [self saveData]; 92 | } 93 | 94 | - (void)emptyList:(SDDownloadModelList)list { 95 | NSString *keyName = nil; 96 | NSMutableArray *array = [self _arrayForListType:list keyName:&keyName]; 97 | NSRange removals = (NSRange){0, array.count}; 98 | [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:removals] forKey:keyName]; 99 | [array removeAllObjects]; 100 | [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:removals] forKey:keyName]; 101 | [self saveData]; 102 | } 103 | 104 | - (void)moveDownload:(SDSafariDownload *)download toList:(SDDownloadModelList)list { 105 | [download retain]; 106 | NSMutableArray *target = [self _arrayForListType:list keyName:NULL]; 107 | if(![target containsObject:download]) { 108 | [self removeDownload:download fromList:(list == SDDownloadModelFinishedList ? SDDownloadModelRunningList : SDDownloadModelFinishedList)]; 109 | [self addDownload:download toList:list]; 110 | } 111 | [download release]; 112 | } 113 | 114 | - (NSIndexPath *)indexPathForDownload:(SDSafariDownload *)download { 115 | int row = -1; 116 | if((row = [_runningDownloads indexOfObjectIdenticalTo:download]) != NSNotFound) { 117 | return [NSIndexPath indexPathForRow:row inSection:0]; 118 | } else if((row = [_finishedDownloads indexOfObjectIdenticalTo:download]) != NSNotFound) { 119 | return [NSIndexPath indexPathForRow:row inSection:1]; 120 | } 121 | return nil; 122 | } 123 | @end 124 | -------------------------------------------------------------------------------- /extension/SDDownloadPromptViewController.h: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDDownloadRequest.h" 3 | #import "SDFileBrowserNavigationController.h" 4 | 5 | @protocol SDDownloadPrompt 6 | @property (nonatomic, retain) SDDownloadRequest *downloadRequest; 7 | @end 8 | 9 | @protocol SDDownloadPromptDelegate 10 | - (void)downloadPrompt:(NSObject *)downloadPrompt didCompleteWithAction:(SDActionType)action; 11 | @end 12 | 13 | @interface SDDownloadPromptViewController : UITableViewController { 14 | NSObject *_delegate; 15 | SDDownloadRequest *_downloadRequest; 16 | NSMutableArray *_supportedActions; 17 | } 18 | @property (nonatomic, assign) NSObject *delegate; 19 | @property (nonatomic, retain) SDDownloadRequest *downloadRequest; 20 | - (id)initWithDownloadRequest:(SDDownloadRequest *)downloadRequest delegate:(id)delegate; 21 | - (void)dismissWithCancel; 22 | @end 23 | -------------------------------------------------------------------------------- /extension/SDDownloadPromptViewController.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SDMCommon.h" 4 | #import "SDDownloadPromptViewController.h" 5 | #import "SDDownloadManager.h" 6 | 7 | #import "SDUserSettings.h" 8 | #import "SDResources.h" 9 | #import "SDFileType.h" 10 | 11 | #import "SDNavigationController.h" 12 | 13 | @implementation SDDownloadPromptViewController 14 | @synthesize delegate = _delegate; 15 | @synthesize downloadRequest = _downloadRequest; 16 | 17 | - (int)panelType { return SDPanelTypeDownloadPrompt; } 18 | 19 | - (UIModalPresentationStyle)modalPresentationStyle { 20 | return SDM$WildCat ? UIModalPresentationFormSheet : UIModalPresentationFullScreen; 21 | } 22 | 23 | - (NSString *)title { 24 | return SDLocalizedString(@"DOWNLOAD_PROMPT_TITLE"); 25 | } 26 | 27 | 28 | - (id)initWithDownloadRequest:(SDDownloadRequest *)downloadRequest delegate:(id)delegate { 29 | if((self = [super initWithStyle:UITableViewStyleGrouped]) != nil) { 30 | self.downloadRequest = downloadRequest; 31 | self.delegate = delegate; 32 | 33 | _supportedActions = [[NSMutableArray alloc] initWithCapacity:3]; 34 | [_supportedActions addObject:[NSNumber numberWithInteger:SDActionTypeDownload]]; 35 | if(_downloadRequest.supportsViewing) 36 | [_supportedActions addObject:[NSNumber numberWithInteger:SDActionTypeView]]; 37 | [_supportedActions addObject:[NSNumber numberWithInteger:SDActionTypeCancel]]; 38 | } return self; 39 | } 40 | 41 | - (void)dealloc { 42 | [_downloadRequest release]; 43 | [_supportedActions release]; 44 | [super dealloc]; 45 | } 46 | 47 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 48 | return indexPath.section == 0 ? 72.f : indexPath.section == 1 ? 54.f : 44.f; 49 | } 50 | 51 | - (int)numberOfSectionsInTableView:(UITableView *)tableView { 52 | return 3; 53 | } 54 | 55 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 56 | switch(section) { 57 | //case 0: 58 | //return SDLocalizedString(@"FILE"); 59 | case 1: 60 | return SDLocalizedString(@"SAVE_INTO"); 61 | //case 2: 62 | //return SDLocalizedString(@"ACTIONS"); 63 | } 64 | return nil; 65 | } 66 | 67 | - (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 68 | switch(section) { 69 | case 0: 70 | return 1; 71 | case 1: 72 | return 1; 73 | case 2: 74 | return [_supportedActions count]; 75 | } 76 | return 0; 77 | } 78 | 79 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 80 | NSString *reuseIdentifier = nil; 81 | UITableViewCellStyle cellStyle = UITableViewCellStyleDefault; 82 | switch(indexPath.section) { 83 | case 0: 84 | reuseIdentifier = @"FilenameCell"; 85 | cellStyle = UITableViewCellStyleSubtitle; 86 | break; 87 | case 1: 88 | reuseIdentifier = @"DestinationCell"; 89 | cellStyle = UITableViewCellStyleSubtitle; 90 | break; 91 | case 2: 92 | reuseIdentifier = @"ActionCell"; 93 | break; 94 | } 95 | 96 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier] ?: [[[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:reuseIdentifier] autorelease]; 97 | 98 | switch(indexPath.section) { 99 | case 0: { 100 | SDFileType *fileType = [SDFileType fileTypeForExtension:[_downloadRequest.filename pathExtension] orMIMEType:_downloadRequest.mimeType]; 101 | cell.imageView.image = [SDResources iconForFileType:fileType]; 102 | cell.textLabel.text = _downloadRequest.filename; 103 | cell.detailTextLabel.text = fileType.name; 104 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 105 | break; 106 | } case 1: { 107 | NSString *savePath = _downloadRequest.savePath; 108 | if([savePath isEqualToString:NSHomeDirectory()]) { 109 | cell.imageView.image = [SDResources imageNamed:@"Icons/home.png"]; 110 | cell.textLabel.text = SDLocalizedString(@"HOME_FOLDER"); 111 | cell.detailTextLabel.text = nil; 112 | } else { 113 | cell.imageView.image = [SDResources iconForFolder]; 114 | cell.textLabel.text = [_downloadRequest.savePath lastPathComponent]; 115 | cell.detailTextLabel.text = [[_downloadRequest.savePath stringByDeletingLastPathComponent] stringByAbbreviatingWithTildeInPath]; 116 | } 117 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 118 | break; 119 | } case 2: 120 | cell.textLabel.textAlignment = UITextAlignmentCenter; 121 | switch(indexPath.row) { 122 | case 0: 123 | cell.textLabel.text = SDLocalizedString(@"ACTION_DOWNLOAD"); 124 | break; 125 | case 1: // If we don't support viewing, we actually fall through to display 'Cancel'. 126 | if(_downloadRequest.supportsViewing) { 127 | cell.textLabel.text = SDLocalizedString(@"ACTION_VIEW"); 128 | break; 129 | } 130 | case 2: 131 | cell.textLabel.text = SDLocalizedString(@"ACTION_CANCEL"); 132 | break; 133 | } 134 | } 135 | return cell; 136 | } 137 | 138 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 139 | switch(indexPath.section) { 140 | case 0: 141 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 142 | break; 143 | case 1: { 144 | SDFileBrowserNavigationController *fileNavController = [[SDFileBrowserNavigationController alloc] initWithMode:SDFileBrowserModeSelectPath downloadRequest:_downloadRequest]; 145 | fileNavController.standalone = YES; 146 | fileNavController.fileBrowserDelegate = self; 147 | // The standalone toggle is a hack. We just don't want to send browserpanel messages when we're presented on-top-of. 148 | [(SDNavigationController *)self.navigationController setStandalone:YES]; 149 | [self presentModalViewController:fileNavController animated:YES]; 150 | [fileNavController release]; 151 | break; 152 | } case 2: 153 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 154 | [_delegate downloadPrompt:self didCompleteWithAction:(SDActionType)[[_supportedActions objectAtIndex:indexPath.row] integerValue]]; 155 | [(SDNavigationController *)[self navigationController] close]; 156 | break; 157 | } 158 | } 159 | 160 | - (void)dismissWithCancel { 161 | [_delegate downloadPrompt:self didCompleteWithAction:SDActionTypeNone]; 162 | [(SDNavigationController *)[self navigationController] close]; 163 | } 164 | 165 | - (void)fileBrowserDidCancel:(SDFileBrowserNavigationController *)fileBrowser { 166 | [self dismissModalViewControllerAnimated:YES]; 167 | [(SDNavigationController *)self.navigationController setStandalone:NO]; 168 | [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] animated:YES]; 169 | } 170 | 171 | - (void)fileBrowser:(SDFileBrowserNavigationController *)fileBrowser didSelectPath:(NSString *)path { 172 | [self dismissModalViewControllerAnimated:YES]; 173 | [(SDNavigationController *)self.navigationController setStandalone:NO]; 174 | [[SDUserSettings sharedInstance] setObject:path forKey:@"DefaultDownloadDirectory"]; 175 | [[SDUserSettings sharedInstance] commit]; 176 | _downloadRequest.savePath = path; 177 | [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone]; 178 | } 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /extension/SDDownloadRequest.h: -------------------------------------------------------------------------------- 1 | @class WebFrame; 2 | 3 | @interface SDDownloadRequest : NSObject { 4 | NSURLRequest *_urlRequest; 5 | NSString *_filename; 6 | NSString *_mimeType; 7 | WebFrame *_webFrame; 8 | NSString *_savePath; 9 | id _context; 10 | } 11 | @property (nonatomic, retain) NSURLRequest *urlRequest; 12 | @property (nonatomic, retain) NSString *filename; 13 | @property (nonatomic, retain) NSString *mimeType; 14 | @property (nonatomic, retain) WebFrame *webFrame; 15 | @property (nonatomic, retain) NSString *savePath; 16 | @property (nonatomic, assign) BOOL supportsViewing; 17 | + (SDDownloadRequest *)pendingRequestForContext:(id)context; 18 | - (id)initWithURLRequest:(NSURLRequest *)request filename:(NSString *)filename mimeType:(NSString *)mimeType webFrame:(WebFrame *)webFrame context:(id)context; 19 | - (void)attachToContext; 20 | - (void)detachFromContext; 21 | - (BOOL)matchesURLRequest:(NSURLRequest *)request; 22 | @end 23 | -------------------------------------------------------------------------------- /extension/SDDownloadRequest.mm: -------------------------------------------------------------------------------- 1 | #import "SDDownloadRequest.h" 2 | #import 3 | 4 | static const NSString *const kSDMAssociatedDownloadRequestKey = @"kSDMAssociatedDownloadRequestKey"; 5 | 6 | @implementation SDDownloadRequest 7 | @synthesize urlRequest = _urlRequest, filename = _filename, mimeType = _mimeType, webFrame = _webFrame, supportsViewing = _supportsViewing; 8 | @synthesize savePath = _savePath; 9 | 10 | + (SDDownloadRequest *)pendingRequestForContext:(id)context { 11 | return objc_getAssociatedObject(context, kSDMAssociatedDownloadRequestKey); 12 | } 13 | 14 | - (id)initWithURLRequest:(NSURLRequest *)request filename:(NSString *)filename mimeType:(NSString *)mimeType webFrame:(WebFrame *)webFrame context:(id)context { 15 | if((self = [super init]) != nil) { 16 | self.urlRequest = request; 17 | self.filename = filename; 18 | self.mimeType = mimeType; 19 | self.webFrame = webFrame; 20 | _context = context; 21 | } return self; 22 | } 23 | 24 | - (void)dealloc { 25 | [_urlRequest release]; 26 | [_filename release]; 27 | [_mimeType release]; 28 | [_webFrame release]; 29 | [_savePath release]; 30 | [super dealloc]; 31 | } 32 | 33 | - (void)attachToContext { 34 | objc_setAssociatedObject(_context, kSDMAssociatedDownloadRequestKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 35 | } 36 | 37 | - (void)detachFromContext { 38 | objc_setAssociatedObject(_context, kSDMAssociatedDownloadRequestKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 39 | } 40 | 41 | - (BOOL)matchesURLRequest:(NSURLRequest *)request { 42 | return [self.urlRequest isEqual:request]; 43 | } 44 | @end 45 | -------------------------------------------------------------------------------- /extension/SDFileBrowserNavigationController.h: -------------------------------------------------------------------------------- 1 | #import "SDNavigationController.h" 2 | #import "SDDownloadRequest.h" 3 | 4 | typedef enum { 5 | SDFileBrowserModeSelectPath = 0, 6 | SDFileBrowserModeImmediateDownload 7 | } SDFileBrowserMode; 8 | 9 | @class SDFileBrowserNavigationController; 10 | @protocol SDFileBrowserDelegate 11 | - (void)fileBrowserDidCancel:(SDFileBrowserNavigationController *)fileBrowser; 12 | - (void)fileBrowser:(SDFileBrowserNavigationController *)fileBrowser didSelectPath:(NSString *)path; 13 | @end 14 | 15 | @interface SDFileBrowserNavigationController : SDNavigationController { 16 | SDFileBrowserMode _mode; 17 | SDDownloadRequest *_downloadRequest; 18 | NSString *_path; 19 | NSArray *_browserToolbarItems; 20 | NSObject *_fileBrowserDelegate; 21 | } 22 | @property (nonatomic, assign) NSObject *fileBrowserDelegate; 23 | @property (nonatomic, retain) SDDownloadRequest *downloadRequest; 24 | @property (nonatomic, copy, readonly) NSString *path; 25 | @property (nonatomic, retain, readonly) NSArray *browserToolbarItems; 26 | - (id)initWithMode:(SDFileBrowserMode)mode; 27 | - (id)initWithMode:(SDFileBrowserMode)mode downloadRequest:(SDDownloadRequest *)downloadRequest; 28 | @end 29 | -------------------------------------------------------------------------------- /extension/SDFileBrowserNavigationController.mm: -------------------------------------------------------------------------------- 1 | #import "SDFileBrowserNavigationController.h" 2 | #import "SDDirectoryListViewController.h" 3 | #import "SDMCommon.h" 4 | 5 | #import "SDResources.h" 6 | #import "SDUserSettings.h" 7 | 8 | @interface SDFileBrowserNavigationController () 9 | @property (nonatomic, copy) NSString *path; 10 | - (NSArray *)_viewControllersForPath:(NSString *)path rootedAt:(NSString *)root; 11 | @end 12 | 13 | @implementation SDFileBrowserNavigationController 14 | @synthesize downloadRequest = _downloadRequest; 15 | @synthesize path = _path; 16 | @synthesize fileBrowserDelegate = _fileBrowserDelegate; 17 | @synthesize browserToolbarItems = _browserToolbarItems; 18 | 19 | - (int)panelType { return SDPanelTypeFileBrowser; } 20 | 21 | - (UIModalPresentationStyle)modalPresentationStyle { 22 | return SDM$WildCat ? UIModalPresentationFormSheet : UIModalPresentationFullScreen; 23 | } 24 | 25 | - (id)initWithMode:(SDFileBrowserMode)mode { 26 | return [self initWithMode:mode downloadRequest:nil]; 27 | } 28 | 29 | - (id)initWithMode:(SDFileBrowserMode)mode downloadRequest:(SDDownloadRequest *)downloadRequest { 30 | if((self = [super init]) != nil) { 31 | self.delegate = self; 32 | self.downloadRequest = downloadRequest; 33 | _mode = mode; 34 | 35 | [self setToolbarHidden:NO animated:NO]; 36 | } return self; 37 | } 38 | 39 | - (void)setDownloadRequest:(SDDownloadRequest *)downloadRequest { 40 | id old = _downloadRequest; 41 | _downloadRequest = [downloadRequest retain]; 42 | [old release]; 43 | self.path = _downloadRequest.savePath; 44 | } 45 | 46 | - (void)dealloc { 47 | [_downloadRequest release]; 48 | [_path release]; 49 | [_browserToolbarItems release]; 50 | [super dealloc]; 51 | } 52 | 53 | - (void)setPath:(NSString *)path { 54 | [_path release]; 55 | _path = [[path stringByStandardizingPath] copy]; 56 | self.viewControllers = [self _viewControllersForPath:path rootedAt:NSHomeDirectory()]; 57 | } 58 | 59 | - (void)viewDidLoad { 60 | [super viewDidLoad]; 61 | NSString *doneButtonTitle = _mode == SDFileBrowserModeImmediateDownload ? SDLocalizedString(@"ACTION_SAVE_AS") : SDLocalizedString(@"ACTION_CHOOSE_DIRECTORY"); 62 | 63 | _browserToolbarItems = [[NSArray arrayWithObjects: 64 | [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(_cancelButtonTapped:)] autorelease], 65 | [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], 66 | [[[UIBarButtonItem alloc] initWithTitle:doneButtonTitle style:UIBarButtonItemStyleDone target:self action:@selector(_doneButtonTapped:)] autorelease], 67 | nil] retain]; 68 | } 69 | 70 | - (void)viewDidUnload { 71 | [super viewDidUnload]; 72 | [_browserToolbarItems release]; 73 | _browserToolbarItems = nil; 74 | } 75 | 76 | - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 77 | if(_mode == SDFileBrowserModeImmediateDownload) { 78 | if(_downloadRequest.filename) 79 | viewController.navigationItem.prompt = [NSString stringWithFormat:SDLocalizedString(@"SAVE_AS_PROMPT_"), _downloadRequest.filename]; 80 | else 81 | viewController.navigationItem.prompt = SDLocalizedString(@"SAVE_AS_PROMPT"); 82 | } 83 | } 84 | 85 | - (NSArray *)_viewControllersForPath:(NSString *)path rootedAt:(NSString *)root { 86 | NSMutableArray *a = [NSMutableArray array]; 87 | root = [root stringByStandardizingPath]; 88 | 89 | int pass = 0; 90 | do { 91 | if(pass > 0) 92 | path = [path stringByDeletingLastPathComponent]; 93 | 94 | SDDirectoryListViewController *directoryViewController = [[SDDirectoryListViewController alloc] initWithPath:path]; 95 | [a insertObject:directoryViewController atIndex:0]; 96 | [directoryViewController release]; 97 | pass++; 98 | } while(![path isEqualToString:root]); 99 | 100 | return a; 101 | } 102 | 103 | - (void)_cancelButtonTapped:(id)sender { 104 | [_fileBrowserDelegate fileBrowserDidCancel:self]; 105 | } 106 | 107 | - (void)_doneButtonTapped:(id)sender { 108 | [_fileBrowserDelegate fileBrowser:self didSelectPath:[(SDDirectoryListViewController *)self.topViewController currentPath]]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /extension/SDMCommon.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __cplusplus 4 | #define _EXTERN extern "C" 5 | #else 6 | #define _EXTERN extern 7 | #endif 8 | 9 | // Exported from Downloader*.xm 10 | _EXTERN Class SDM$BrowserController; 11 | 12 | // Exported from Downloader*.xm 13 | _EXTERN bool SDM$WildCat; 14 | 15 | // Exported from SDDownloadManager.mm 16 | _EXTERN NSString * const kSDMAssociatedOverrideAuthenticationChallenge; 17 | 18 | // Exported from _CustomToolbar_Old.xm 19 | _EXTERN NSString * const kSDMAssociatedPortraitDownloadButton; 20 | _EXTERN NSString * const kSDMAssociatedActionButton; 21 | _EXTERN NSString * const kSDMAssociatedBookmarksButton; 22 | 23 | // Exported from _CustomToolbar_iOS5.xm 24 | _EXTERN NSString * const kSDMAssociatedPresentationViewKey; 25 | 26 | typedef enum { 27 | SDPanelTypeDownloadManager = 150, 28 | SDPanelTypeDownloadPrompt = 151, 29 | SDPanelTypeFileBrowser = 152 30 | } SDPanelTypes; 31 | 32 | typedef enum 33 | { 34 | SDActionTypeNone = 0, 35 | SDActionTypeView = 1, 36 | SDActionTypeDownload = 2, 37 | SDActionTypeCancel = 3, 38 | SDActionTypeDownloadAs = 4, 39 | } SDActionType; 40 | -------------------------------------------------------------------------------- /extension/SDMVersioning.h: -------------------------------------------------------------------------------- 1 | #define _SDM_iOS_2_0 kCFCoreFoundationVersionNumber_iPhoneOS_2_0 2 | #define _SDM_iOS_2_1 kCFCoreFoundationVersionNumber_iPhoneOS_2_1 3 | #define _SDM_iOS_2_2 kCFCoreFoundationVersionNumber_iPhoneOS_2_2 4 | #define _SDM_iOS_3_0 kCFCoreFoundationVersionNumber_iPhoneOS_3_0 5 | #define _SDM_iOS_3_1 kCFCoreFoundationVersionNumber_iPhoneOS_3_1 6 | #define _SDM_iOS_3_2 kCFCoreFoundationVersionNumber_iPhoneOS_3_2 7 | #define _SDM_iOS_4_0 kCFCoreFoundationVersionNumber_iOS_4_0 8 | #define _SDM_iOS_4_1 kCFCoreFoundationVersionNumber_iOS_4_1 9 | #define _SDM_iOS_4_2 kCFCoreFoundationVersionNumber_iOS_4_2 10 | #define _SDM_iOS_5_0 675.00 11 | 12 | #define SDMSystemVersionEQ(v) (kCFCoreFoundationVersionNumber == v) 13 | #define SDMSystemVersionLT(v) (kCFCoreFoundationVersionNumber < v) 14 | #define SDMSystemVersionLE(v) (kCFCoreFoundationVersionNumber <= v) 15 | #define SDMSystemVersionGT(v) (kCFCoreFoundationVersionNumber > v) 16 | #define SDMSystemVersionGE(v) (kCFCoreFoundationVersionNumber >= v) 17 | -------------------------------------------------------------------------------- /extension/SDNavigationController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SDNavigationController 3 | * SDM 4 | * Dustin Howett 2012-01-30 5 | */ 6 | 7 | #import "WebUI/BrowserPanel.h" 8 | 9 | @interface SDNavigationController : UINavigationController { 10 | BOOL _standalone; 11 | } 12 | @property (nonatomic, assign) BOOL standalone; 13 | - (void)close; 14 | @end 15 | -------------------------------------------------------------------------------- /extension/SDNavigationController.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * SDNavigationController 3 | * SDM 4 | * Dustin Howett 2012-01-30 5 | */ 6 | 7 | #import "SDMCommon.h" 8 | #import "SDMVersioning.h" 9 | #import "SDNavigationController.h" 10 | 11 | #import "Safari/BrowserController.h" 12 | 13 | @protocol SDNavigationControllerSubPanel 14 | - (int)panelType; 15 | @end 16 | 17 | @implementation SDNavigationController 18 | @synthesize standalone = _standalone; 19 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation { 20 | return SDM$WildCat ? YES : (orientation != UIInterfaceOrientationPortraitUpsideDown); 21 | } 22 | 23 | - (UIViewController *)rootViewController { 24 | return [self.viewControllers objectAtIndex:0]; 25 | } 26 | 27 | - (BOOL)allowsRotation { return YES; } 28 | - (BOOL)pausesPages { return NO; } 29 | - (int)panelType { return [[self rootViewController] panelType]; } 30 | - (int)panelState { return 1; } 31 | - (BOOL)shouldShowButtonBar { return SDM$WildCat ? YES : NO; } 32 | - (BOOL)isDismissible { return NO; } // Maybe? 33 | - (BOOL)disablesStatusBarPress { return NO; } 34 | - (BOOL)ignoresPrivateBrowsingStyle { return YES; } 35 | 36 | - (void)close { 37 | [[SDM$BrowserController sharedBrowserController] hideBrowserPanelType:[self panelType]]; 38 | } 39 | 40 | - (void)viewDidAppear:(BOOL)animated { 41 | [super viewDidAppear:animated]; 42 | if(!_standalone) 43 | [[SDM$BrowserController sharedBrowserController] didShowBrowserPanel:[[SDM$BrowserController sharedBrowserController] browserPanel]]; 44 | } 45 | - (void)viewWillAppear:(BOOL)animated { 46 | [super viewWillAppear:animated]; 47 | if(!_standalone) 48 | [[SDM$BrowserController sharedBrowserController] willShowBrowserPanel:[[SDM$BrowserController sharedBrowserController] browserPanel]]; 49 | } 50 | - (void)viewDidDisappear:(BOOL)animated { 51 | [super viewDidDisappear:animated]; 52 | if(!_standalone) 53 | if ([[SDM$BrowserController sharedBrowserController] browserPanel] == self) 54 | [[SDM$BrowserController sharedBrowserController] didHideBrowserPanel:[[SDM$BrowserController sharedBrowserController] browserPanel]]; 55 | } 56 | - (void)viewWillDisappear:(BOOL)animated { 57 | [super viewWillDisappear:animated]; 58 | if(!_standalone) 59 | if([[SDM$BrowserController sharedBrowserController] browserPanel] == self) 60 | [[SDM$BrowserController sharedBrowserController] closeBrowserPanel:[[SDM$BrowserController sharedBrowserController] browserPanel]]; 61 | } 62 | 63 | - (void)didHideBrowserPanel { 64 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) 65 | [[[[SDM$BrowserController sharedBrowserController] _modalViewController] view] performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0]; 66 | } 67 | 68 | - (CGSize)contentSizeForViewInPopover { 69 | return CGSizeMake(320.f, 480.f); 70 | } 71 | 72 | - (UIModalPresentationStyle)modalPresentationStyle { 73 | return [[self rootViewController] modalPresentationStyle]; 74 | } 75 | @end 76 | 77 | // vim:filetype=objc:ts=8:sw=8:noet 78 | -------------------------------------------------------------------------------- /extension/SDSafariDownload.h: -------------------------------------------------------------------------------- 1 | // 2 | // SDSafariDownload.h 3 | // SDSafariDownload 4 | // 5 | // Created by Youssef Francis on 7/21/09. 6 | // Copyright 2009 Brancipater Software. All rights reserved. 7 | // 8 | // Dustin L. Howett (2012-01-31) 9 | 10 | #import 11 | #import "NSURLDownload.h" 12 | #import "SDConcurrentOperation.h" 13 | 14 | typedef enum { 15 | SDDownloadStatusWaiting = 0, 16 | SDDownloadStatusAuthenticationWaiting, 17 | SDDownloadStatusRunning, 18 | SDDownloadStatusPaused, 19 | SDDownloadStatusCompleted, 20 | SDDownloadStatusCancelled, 21 | SDDownloadStatusFailed, 22 | SDDownloadStatusRetrying 23 | } SDDownloadStatus; 24 | 25 | extern NSString * const kSDSafariDownloadTemporaryDirectory; 26 | 27 | @protocol SDSafariDownloadDelegate; 28 | 29 | /* 30 | * WHAT DO I NEED: 31 | * A status 32 | * A start date. 33 | * A filename. 34 | * A directory. 35 | * A temporary filename because the download could have a different name. 36 | * A temporary path (/tmp/.partial) 37 | * Number of total bytes. 38 | * Number of downloaded bytes so far. 39 | * Number of downloaded bytes this session. 40 | * ^^^^^^ What byte we began the download from 41 | * Progress can be calculated from that. 42 | * Time remaining can be calculated from that. 43 | * 44 | * A URL Request. 45 | * Whether or not we're using the suggested filename? 46 | * How many times we have retried. 47 | * The last error that occurred. 48 | * 49 | * RESUME DATA. 50 | * Whether authentication is required. 51 | * Authentication Credentials (maybe) 52 | * The downloader object. 53 | * The URL Response (headers, why not?) 54 | * 55 | * WHAT NEEDS TO BE SERIALIZED? WHAT NEEDS TO DISPATCH CHANGE NOTIFICATIONS? 56 | */ 57 | 58 | @interface SDSafariDownload : SDConcurrentOperation 59 | { 60 | // Of interest to Outsiders 61 | SDDownloadStatus _status; // Serialize. 62 | BOOL _useSuggestedFilename; // Serialize. 63 | NSString *_filename; // Serialize. 64 | NSString *_mimeType; // Serialize. 65 | NSString *_path; // Serialize. 66 | NSString *_temporaryPath; // Serialize. 67 | NSDate *_startDate; // Serialize. 68 | unsigned long long _totalBytes; // Serialize. 69 | unsigned long long _downloadedBytes; // Serialize. 70 | unsigned int _retryCount; 71 | NSError *_lastError; 72 | 73 | unsigned long long _startedFromByte; 74 | NSURLRequest *_URLRequest; 75 | NSURLResponse *_URLResponse; 76 | 77 | NSMutableDictionary *_resumeData; // Serialize. 78 | BOOL _requiresAuthentication; 79 | NSURLCredential *_authenticationCredential; 80 | 81 | NSURLDownload *_downloader; 82 | } 83 | 84 | @property (nonatomic, readonly, assign) SDDownloadStatus status; 85 | @property (nonatomic, assign) BOOL useSuggestedFilename; 86 | @property (nonatomic, retain) NSString *filename; 87 | @property (nonatomic, retain) NSString *mimeType; 88 | @property (nonatomic, retain) NSString *path; 89 | @property (nonatomic, readonly, retain) NSString *temporaryPath; 90 | @property (nonatomic, readonly, retain) NSDate *startDate; 91 | @property (nonatomic, readonly, assign) unsigned long long totalBytes; 92 | @property (nonatomic, readonly, assign) unsigned long long downloadedBytes; 93 | @property (nonatomic, readonly, assign) unsigned int retryCount; 94 | @property (nonatomic, readonly, retain) NSError *lastError; 95 | @property (nonatomic, readonly, assign) unsigned long long startedFromByte; 96 | @property (nonatomic, retain) NSURLRequest *URLRequest; 97 | @property (nonatomic, readonly, retain) NSURLResponse *URLResponse; 98 | @property (nonatomic, readonly, retain) NSMutableDictionary *resumeData; 99 | @property (nonatomic, readonly, assign) BOOL requiresAuthentication; 100 | @property (nonatomic, readonly, retain) NSURLCredential *authenticationCredential; 101 | @property (nonatomic, readonly, retain) NSURLDownload *downloader; 102 | 103 | @property (nonatomic, assign) NSObject *delegate; 104 | 105 | - (id)initWithDownload:(SDSafariDownload *)download; 106 | 107 | /* 108 | - (id)initWithRequest:(NSURLRequest*)urlRequest name:(NSString *)name delegate:(id)del useSuggested:(BOOL)use; 109 | - (void)downloadFailedWithError:(NSError *)err; 110 | - (void)setProgress:(float)prog speed:(float)spd; 111 | - (void)downloadStarted; 112 | - (void)setRetryString:(NSString*)status; 113 | */ 114 | 115 | @end 116 | 117 | @protocol SDSafariDownloadDelegate 118 | @optional 119 | - (void)downloadDidChangeStatus:(SDSafariDownload *)download; 120 | - (void)downloadDidReceiveData:(SDSafariDownload *)download; 121 | - (void)downloadDidUpdateMetadata:(SDSafariDownload *)download; 122 | - (NSString *)uniqueFilenameForDownload:(SDSafariDownload *)download withSuggestion:(NSString *)suggestedFilename; 123 | - (BOOL)downloadShouldRetry:(SDSafariDownload *)download; 124 | - (float)retryDelayForDownload:(SDSafariDownload *)download; 125 | - (void)download:(SDSafariDownload *)download didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 126 | 127 | /* 128 | - (void)downloadDidBegin:(SDSafariDownload*)download; 129 | - (void)downloadDidProvideFilename:(SDSafariDownload*)download; 130 | - (void)downloadDidFinish:(SDSafariDownload*)download; 131 | - (void)downloadDidUpdate:(SDSafariDownload*)download; 132 | - (void)downloadDidFail:(SDSafariDownload*)download; 133 | - (void)downloadDidCancel:(SDSafariDownload*)download; 134 | - (void)downloadWillRetry:(SDSafariDownload*)download; 135 | */ 136 | 137 | @end 138 | 139 | //@interface MyAuthenticationView : AuthenticationView 140 | //- (void)setSavedChallenge:(id)savedChallenge; 141 | //@end 142 | -------------------------------------------------------------------------------- /extension/SDSafariDownload.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * SDSafariDownload 3 | * SDM 4 | * 5 | * Dustin L. Howett 6 | * 2012-02-01 7 | */ 8 | #import "SDMCommon.h" 9 | #import "SDSafariDownload.h" 10 | #import 11 | #import 12 | 13 | NSString * const kSDSafariDownloadTemporaryDirectory = @"/tmp/.partial"; 14 | 15 | @interface SDSafariDownload () 16 | @property (nonatomic, assign, readwrite) SDDownloadStatus status; 17 | //@property (nonatomic, retain) NSString *filename; 18 | //@property (nonatomic, retain) NSString *path; 19 | @property (nonatomic, retain, readwrite) NSString *temporaryPath; 20 | @property (nonatomic, retain, readwrite) NSDate *startDate; 21 | @property (nonatomic, assign, readwrite) unsigned long long totalBytes; 22 | @property (nonatomic, assign, readwrite) unsigned long long downloadedBytes; 23 | @property (nonatomic, assign, readwrite) unsigned int retryCount; 24 | @property (nonatomic, retain, readwrite) NSError *lastError; 25 | @property (nonatomic, assign, readwrite) unsigned long long startedFromByte; 26 | //@property (nonatomic, retain) NSURLRequest *URLRequest; 27 | @property (nonatomic, retain, readwrite) NSURLResponse *URLResponse; 28 | @property (nonatomic, retain, readwrite) NSMutableDictionary *resumeData; 29 | @property (nonatomic, assign, readwrite) BOOL requiresAuthentication; 30 | @property (nonatomic, retain, readwrite) NSURLCredential *authenticationCredential; 31 | @property (nonatomic, retain, readwrite) NSURLDownload *downloader; 32 | - (BOOL)_begin; 33 | @end 34 | 35 | @implementation SDSafariDownload 36 | @synthesize status = _status, filename = _filename, 37 | path = _path, temporaryPath = _temporaryPath, 38 | startDate = _startDate, totalBytes = _totalBytes, 39 | downloadedBytes = _downloadedBytes, retryCount = _retryCount, 40 | lastError = _lastError, startedFromByte = _startedFromByte, 41 | URLRequest = _URLRequest, URLResponse = _URLResponse, 42 | resumeData = _resumeData, requiresAuthentication = _requiresAuthentication, 43 | authenticationCredential = _authenticationCredential, 44 | useSuggestedFilename = _useSuggestedFilename, mimeType = _mimeType, 45 | downloader = _downloader, delegate = _delegate; 46 | 47 | - (id)init { 48 | if((self = [super init]) != nil) { 49 | 50 | } return self; 51 | } 52 | 53 | - (id)initWithDownload:(SDSafariDownload *)download { 54 | if((self = [self init]) != nil) { 55 | _status = download.status; 56 | _useSuggestedFilename = download.useSuggestedFilename; 57 | self.filename = download.filename; 58 | self.mimeType = download.mimeType; 59 | self.path = download.path; 60 | self.temporaryPath = download.temporaryPath; 61 | self.startDate = download.startDate; 62 | _totalBytes = download.totalBytes; 63 | _downloadedBytes = download.downloadedBytes; 64 | _retryCount = download.retryCount; 65 | self.lastError = download.lastError; 66 | _startedFromByte = download.startedFromByte; 67 | self.URLRequest = download.URLRequest; 68 | self.URLResponse = download.URLResponse; 69 | self.resumeData = download.resumeData; 70 | _requiresAuthentication = download.requiresAuthentication; 71 | self.authenticationCredential = download.authenticationCredential; 72 | 73 | self.delegate = download.delegate; // Important. 74 | } return self; 75 | } 76 | 77 | - (void)dealloc { 78 | [_filename release]; 79 | [_path release]; 80 | [_temporaryPath release]; 81 | [_startDate release]; 82 | [_lastError release]; 83 | [_URLRequest release]; 84 | [_URLResponse release]; 85 | [_resumeData release]; 86 | [_authenticationCredential release]; 87 | [_downloader release]; 88 | [_mimeType release]; 89 | [super dealloc]; 90 | } 91 | 92 | - (void)encodeWithCoder:(NSCoder *)encoder { 93 | [super encodeWithCoder:encoder]; 94 | [encoder encodeInt:_status forKey:@"status"]; 95 | [encoder encodeObject:_filename forKey:@"filename"]; 96 | [encoder encodeObject:_path forKey:@"path"]; 97 | [encoder encodeObject:_temporaryPath forKey:@"temporaryPath"]; 98 | [encoder encodeObject:_startDate forKey:@"startDate"]; 99 | [encoder encodeInt64:_totalBytes forKey:@"totalBytes"]; 100 | [encoder encodeInt64:_downloadedBytes forKey:@"downloadedBytes"]; 101 | [encoder encodeObject:_URLRequest forKey:@"URLRequest"]; 102 | [encoder encodeObject:_resumeData forKey:@"resumeData"]; 103 | [encoder encodeObject:_mimeType forKey:@"mimeType"]; 104 | [encoder encodeBool:_useSuggestedFilename forKey:@"useSuggestedFilename"]; 105 | } 106 | 107 | - (id)initWithCoder:(NSCoder *)decoder { 108 | self = [super initWithCoder:decoder]; 109 | if(!self) return nil; 110 | 111 | self.status = (SDDownloadStatus)[decoder decodeIntForKey:@"status"]; 112 | self.filename = [decoder decodeObjectForKey:@"filename"]; 113 | self.path = [decoder decodeObjectForKey:@"path"]; 114 | self.temporaryPath = [decoder decodeObjectForKey:@"temporaryPath"]; 115 | self.startDate = [decoder decodeObjectForKey:@"startDate"]; 116 | self.totalBytes = [decoder decodeInt64ForKey:@"totalBytes"]; 117 | self.downloadedBytes = [decoder decodeInt64ForKey:@"downloadedBytes"]; 118 | self.URLRequest = [decoder decodeObjectForKey:@"URLRequest"]; 119 | self.resumeData = [decoder decodeObjectForKey:@"resumeData"]; 120 | self.mimeType = [decoder decodeObjectForKey:@"mimeType"]; 121 | self.useSuggestedFilename = [decoder decodeBoolForKey:@"useSuggestedFilename"]; 122 | return self; 123 | } 124 | 125 | - (void)setStatus:(SDDownloadStatus)status { 126 | _status = status; 127 | [_delegate downloadDidChangeStatus:self]; 128 | } 129 | 130 | - (void)setTotalBytes:(unsigned long long)totalBytes { 131 | _totalBytes = totalBytes; 132 | [_delegate downloadDidUpdateMetadata:self]; 133 | } 134 | 135 | - (NSString *)_temporaryPathForFilename:(NSString *)filename { 136 | return [kSDSafariDownloadTemporaryDirectory stringByAppendingPathComponent:filename]; 137 | } 138 | 139 | - (void)_createTemporaryDirectory { 140 | [[NSFileManager defaultManager] createDirectoryAtPath:kSDSafariDownloadTemporaryDirectory withIntermediateDirectories:YES attributes:nil error:NULL]; 141 | } 142 | 143 | /* {{{ NSURLDownloadDelegate */ 144 | - (void)download:(NSURLDownload *)download didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 145 | self.status = SDDownloadStatusAuthenticationWaiting; 146 | [_delegate download:self didReceiveAuthenticationChallenge:challenge]; 147 | } 148 | 149 | - (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename { 150 | if(_useSuggestedFilename && _filename) return; 151 | if(_startedFromByte > 0) return; // Do not rename resumed files. 152 | self.filename = [_delegate uniqueFilenameForDownload:self withSuggestion:filename]; 153 | self.temporaryPath = [self _temporaryPathForFilename:_filename]; 154 | [download setDestination:_temporaryPath allowOverwrite:NO]; 155 | [_delegate downloadDidUpdateMetadata:self]; 156 | } 157 | 158 | - (void)downloadDidBegin:(NSURLDownload *)download { 159 | self.startDate = [NSDate date]; 160 | self.status = SDDownloadStatusWaiting; 161 | } 162 | 163 | - (void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path { 164 | // TODO: This seems pointless to do: 165 | // self.temporaryPath = path; 166 | } 167 | 168 | - (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response { 169 | // Get the Etag for the resume data. 170 | // Get the expected content length. 171 | // Set the start date again? Why? 172 | // Save our response. 173 | NSLog(@"Download got URL response %@", response); 174 | if([response isKindOfClass:[NSHTTPURLResponse class]]) { 175 | NSDictionary *headerFields = [(NSHTTPURLResponse *)response allHeaderFields]; 176 | NSString *eTag = [headerFields objectForKey:@"Etag"]; 177 | NSURL *url = _URLRequest.URL; 178 | NSLog(@"URL: %@, eTAG: %@", url, eTag); 179 | if(url && eTag) { 180 | self.resumeData = [NSMutableDictionary dictionary]; 181 | [_resumeData setObject:eTag forKey:@"NSURLDownloadEntityTag"]; 182 | if([headerFields objectForKey:@"Last-Modified"]) { 183 | [_resumeData setObject:[headerFields objectForKey:@"Last-Modified"] forKey:@"NSURLDownloadServerModificationDate"]; 184 | } 185 | [_resumeData setObject:[url absoluteString] forKey:@"NSURLDownloadURL"]; 186 | } 187 | NSLog(@"Download saved resume data %@", _resumeData); 188 | } 189 | if([response expectedContentLength] == NSURLResponseUnknownLength) { 190 | self.totalBytes = ULLONG_MAX; 191 | } else { 192 | self.totalBytes = [response expectedContentLength]; 193 | } 194 | _startedFromByte = 0; 195 | self.URLResponse = response; 196 | self.status = SDDownloadStatusRunning; 197 | if(!_mimeType) { 198 | self.mimeType = response.MIMEType; 199 | [_delegate downloadDidUpdateMetadata:self]; 200 | } 201 | } 202 | 203 | - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(NSUInteger)length { 204 | _downloadedBytes += length; 205 | // TODO: Throttle? 206 | } 207 | 208 | - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType { 209 | return NO; 210 | } 211 | 212 | - (void)download:(NSURLDownload *)download willResumeWithResponse:(NSURLResponse *)response fromByte:(long long)startingByte { 213 | // We already know the size, so we do not need to recalculate it. 214 | self.URLResponse = response; 215 | self.startedFromByte = startingByte; 216 | self.status = SDDownloadStatusRunning; 217 | } 218 | 219 | - (NSURLRequest *)download:(NSURLDownload *)download willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { 220 | // Maybe set our request to the updated request? 221 | return request; 222 | } 223 | 224 | - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error { 225 | NSLog(@"Failed with error: %@", error); 226 | NSInteger errorCode = error.code; 227 | 228 | if((errorCode != NSURLErrorCancelled 229 | && errorCode != NSURLErrorBadURL 230 | && errorCode != NSURLErrorUnsupportedURL 231 | && errorCode != NSURLErrorDataLengthExceedsMaximum 232 | && errorCode != NSURLErrorHTTPTooManyRedirects 233 | && errorCode != NSURLErrorNotConnectedToInternet 234 | && errorCode != NSURLErrorUserCancelledAuthentication 235 | && errorCode != NSURLErrorUserAuthenticationRequired 236 | && errorCode != NSURLErrorZeroByteResource 237 | && errorCode != NSURLErrorNoPermissionsToReadFile 238 | && errorCode != NSURLErrorCannotCreateFile 239 | && errorCode != NSURLErrorCannotOpenFile 240 | && errorCode != NSURLErrorCannotWriteToFile 241 | && errorCode != NSURLErrorCannotCloseFile 242 | && errorCode != NSURLErrorCannotRemoveFile 243 | && errorCode != NSURLErrorCannotMoveFile) 244 | && [_delegate downloadShouldRetry:self]) { 245 | _retryCount++; 246 | self.status = SDDownloadStatusRetrying; 247 | 248 | float wait = [_delegate retryDelayForDownload:self]; 249 | if(wait > 0.f) { 250 | // Literally just block the thread until it's our turn. 251 | usleep((int)(wait * 1000000)); 252 | } 253 | [self _begin]; 254 | } else { 255 | self.status = SDDownloadStatusFailed; 256 | self.finished = YES; 257 | } 258 | } 259 | 260 | - (void)downloadDidFinish:(NSURLDownload *)download { 261 | NSString *finalDestination4 = [self.path stringByAppendingPathComponent:self.filename]; 262 | [SandCastle createDirectoryAtResolvedPath:self.path]; 263 | [SandCastle moveTemporaryFile:self.temporaryPath toResolvedPath:finalDestination4]; 264 | self.resumeData = nil; 265 | self.status = SDDownloadStatusCompleted; 266 | self.finished = YES; 267 | } 268 | /* }}} */ 269 | 270 | - (void)_deleteData { 271 | self.resumeData = nil; 272 | if(self.temporaryPath) 273 | [[NSFileManager defaultManager] removeItemAtPath:self.temporaryPath error:NULL]; 274 | } 275 | 276 | - (BOOL)_resume { 277 | if(!_resumeData) return NO; 278 | self.temporaryPath = [self _temporaryPathForFilename:self.filename]; 279 | if(access([_temporaryPath UTF8String], F_OK) != 0) { 280 | // Temporary file is dead, jim. 281 | self.resumeData = nil; 282 | return NO; 283 | } 284 | 285 | struct stat statbuf; 286 | stat([_temporaryPath UTF8String], &statbuf); 287 | _downloadedBytes = statbuf.st_size; 288 | 289 | // Truncate the file to the last resume data snapshot. 290 | [_resumeData setObject:[NSNumber numberWithUnsignedLongLong:_downloadedBytes] forKey:@"NSURLDownloadBytesReceived"]; 291 | self.startedFromByte = _downloadedBytes; 292 | 293 | NSData *resumeDataSerialization = [NSPropertyListSerialization dataFromPropertyList:_resumeData format:NSPropertyListXMLFormat_v1_0 errorDescription:NULL]; 294 | self.downloader = [[[NSURLDownload alloc] initWithResumeData:resumeDataSerialization delegate:self path:_temporaryPath] autorelease]; 295 | if(!self.downloader) { 296 | self.resumeData = nil; 297 | return NO; 298 | } 299 | 300 | self.downloader.deletesFileUponFailure = NO; 301 | return YES; 302 | } 303 | 304 | - (BOOL)_begin { 305 | self.status = SDDownloadStatusWaiting; 306 | if([self _resume]) return YES; 307 | 308 | [self _deleteData]; 309 | self.downloader = [[[NSURLDownload alloc] initWithRequest:self.URLRequest delegate:self] autorelease]; 310 | if(!self.downloader) return NO; 311 | self.downloader.deletesFileUponFailure = NO; 312 | if(_filename && _useSuggestedFilename) { 313 | self.temporaryPath = [self _temporaryPathForFilename:self.filename]; 314 | [_downloader setDestination:self.temporaryPath allowOverwrite:YES]; 315 | } 316 | 317 | self.totalBytes = 0; 318 | _downloadedBytes = 0; 319 | 320 | return YES; 321 | } 322 | 323 | - (void)main { 324 | NSLog(@"Attempting to begin download."); 325 | [self _createTemporaryDirectory]; 326 | if([self _begin] == NO) { 327 | NSLog(@"Begin failed"); 328 | [self complete]; 329 | // TODO: Notify Owner that we Failed. 330 | return; 331 | } 332 | NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 333 | do { 334 | [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.01]]; 335 | } while(!self.finished && ![self isCancelled]); 336 | if([self isCancelled]) { 337 | [self.downloader cancel]; 338 | [self _deleteData]; 339 | self.status = SDDownloadStatusCancelled; 340 | } 341 | [self complete]; 342 | // TODO: Kill heartbeat timer. 343 | } 344 | 345 | @end 346 | -------------------------------------------------------------------------------- /extension/SDUtils.h: -------------------------------------------------------------------------------- 1 | @interface SDUtils : NSObject 2 | + (NSString *)formatSize:(double)size; 3 | @end 4 | -------------------------------------------------------------------------------- /extension/SDUtils.mm: -------------------------------------------------------------------------------- 1 | #import "SDUtils.h" 2 | #import "SDResources.h" 3 | @implementation SDUtils 4 | + (NSString *)formatSize:(double)size { 5 | if(size < 1024.) return [NSString stringWithFormat:SDLocalizedString(@"SIZE_BYTES"), size]; 6 | size /= 1024.; 7 | if(size < 1024.) return [NSString stringWithFormat:SDLocalizedString(@"SIZE_KILOBYTES"), size]; 8 | size /= 1024.; 9 | return [NSString stringWithFormat:SDLocalizedString(@"SIZE_MEGABYTES"), size]; 10 | } 11 | @end 12 | -------------------------------------------------------------------------------- /extension/UIWebElementAction.h: -------------------------------------------------------------------------------- 1 | @interface UIWebElementAction : NSString { 2 | NSString *_title; // 4 = 0x4 3 | id _actionHandler; // 8 = 0x8 4 | int _type; // 12 = 0xc 5 | } 6 | @property(readonly, assign, nonatomic) int type; // G=0x244ec1; @synthesize=_type 7 | + (id)customElementActionWithTitle:(id)title actionHandler:(id)handler; // 0x244945 8 | + (id)standardElementActionWithType:(int)type; // 0x244e15 9 | + (id)standardElementActionWithType:(int)type customTitle:(id)title; // 0x2449d9 10 | - (id)initWithTitle:(id)title actionHandler:(id)handler type:(int)type; // 0x24485d 11 | - (void)_runActionWithElement:(id)element targetURL:(id)url documentView:(id)view interactionLocation:(CGPoint)location; // 0x244e39 12 | - (id)_title; // 0x244e29 13 | - (void)dealloc; // 0x2448e5 14 | // declared property getter: - (int)type; // 0x244ec1 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /extension/WebPolicyDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 | * its contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | 31 | @class NSError; 32 | @class NSURLResponse; 33 | @class NSURLRequest; 34 | @class WebView; 35 | @class WebFrame; 36 | @class WebPolicyPrivate; 37 | 38 | 39 | /*! 40 | @enum WebNavigationType 41 | @abstract The type of action that triggered a possible navigation. 42 | @constant WebNavigationTypeLinkClicked A link with an href was clicked. 43 | @constant WebNavigationTypeFormSubmitted A form was submitted. 44 | @constant WebNavigationTypeBackForward The user chose back or forward. 45 | @constant WebNavigationTypeReload The User hit the reload button. 46 | @constant WebNavigationTypeFormResubmitted A form was resubmitted (by virtue of doing back, forward or reload). 47 | @constant WebNavigationTypeOther Navigation is taking place for some other reason. 48 | */ 49 | 50 | typedef enum { 51 | WebNavigationTypeLinkClicked, 52 | WebNavigationTypeFormSubmitted, 53 | WebNavigationTypeBackForward, 54 | WebNavigationTypeReload, 55 | WebNavigationTypeFormResubmitted, 56 | WebNavigationTypeOther 57 | } WebNavigationType; 58 | 59 | 60 | extern NSString *WebActionNavigationTypeKey; // NSNumber (WebNavigationType) 61 | extern NSString *WebActionElementKey; // NSDictionary of element info 62 | extern NSString *WebActionButtonKey; // NSEventType 63 | extern NSString *WebActionModifierFlagsKey; // NSNumber (unsigned) 64 | extern NSString *WebActionOriginalURLKey; // NSURL 65 | 66 | 67 | /*! 68 | @protocol WebPolicyDecisionListener 69 | @discussion This protocol is used to call back with the results of a 70 | policy decision. This provides the ability to make these decisions 71 | asyncrhonously, which means the decision can be made by prompting 72 | with a sheet, for example. 73 | */ 74 | 75 | @protocol WebPolicyDecisionListener 76 | 77 | /*! 78 | @method use 79 | @abstract Use the resource 80 | @discussion If there remain more policy decisions to be made, then 81 | the next policy delegate method gets to decide. This will be 82 | either the next navigation policy delegate if there is a redirect, 83 | or the content policy delegate. If there are no more policy 84 | decisions to be made, the resource will be displayed inline if 85 | possible. If there is no view available to display the resource 86 | inline, then unableToImplementPolicyWithError:frame: will be 87 | called with an appropriate error. 88 | 89 |

If a new window is going to be created for this navigation as a 90 | result of frame targetting, then it will be created once you call 91 | this method. 92 | */ 93 | - (void)use; 94 | /*! 95 | @method download 96 | @abstract Download the resource instead of displaying it. 97 | @discussion This method is more than just a convenience because it 98 | allows an in-progress navigation to be converted to a download 99 | based on content type, without having to stop and restart the 100 | load. 101 | */ 102 | - (void)download; 103 | 104 | /*! 105 | @method ignore 106 | @abstract Do nothing (but the client may choose to handle the request itself) 107 | @discussion A policy of ignore prevents WebKit from doing anything 108 | further with the load, however, the client is still free to handle 109 | the request in some other way, such as opening a new window, 110 | opening a new window behind the current one, opening the URL in an 111 | external app, revealing the location in Finder if a file URL, etc. 112 | */ 113 | - (void)ignore; 114 | 115 | @end 116 | 117 | 118 | /*! 119 | @category WebPolicyDelegate 120 | @discussion While loading a URL, WebKit asks the WebPolicyDelegate for 121 | policies that determine the action of what to do with the URL or the data that 122 | the URL represents. Typically, the policy handler methods are called in this order: 123 | 124 | decidePolicyForNewWindowAction:request:newFrameName:decisionListener: (at most once)
125 | decidePolicyForNavigationAction:request:frame:decisionListener: (zero or more times)
126 | decidePolicyForMIMEType:request:frame: (zero or more times)
127 | 128 | New window policy is always checked. Navigation policy is checked 129 | for the initial load and every redirect unless blocked by an 130 | earlier policy. Content policy is checked once the content type is 131 | known, unless an earlier policy prevented it. 132 | 133 | In rare cases, content policy might be checked more than 134 | once. This occurs when loading a "multipart/x-mixed-replace" 135 | document, also known as "server push". In this case, multiple 136 | documents come in one navigation, with each replacing the last. In 137 | this case, conent policy will be checked for each one. 138 | */ 139 | @interface NSObject (WebPolicyDelegate) 140 | 141 | /*! 142 | @method webView:decidePolicyForNavigationAction:request:frame:decisionListener: 143 | @abstract This method is called to decide what to do with a proposed navigation. 144 | @param actionInformation Dictionary that describes the action that triggered this navigation. 145 | @param request The request for the proposed navigation 146 | @param frame The WebFrame in which the navigation is happening 147 | @param listener The object to call when the decision is made 148 | @discussion This method will be called before loading starts, and 149 | on every redirect. 150 | */ 151 | - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation 152 | request:(NSURLRequest *)request 153 | frame:(WebFrame *)frame 154 | decisionListener:(id)listener; 155 | 156 | /*! 157 | @method webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: 158 | @discussion This method is called to decide what to do with an targetted nagivation that would open a new window. 159 | @param actionInformation Dictionary that describes the action that triggered this navigation. 160 | @param request The request for the proposed navigation 161 | @param frame The frame in which the navigation is taking place 162 | @param listener The object to call when the decision is made 163 | @discussion This method is provided so that modified clicks on a targetted link which 164 | opens a new frame can prevent the new window from being opened if they decide to 165 | do something else, like download or present the new frame in a specialized way. 166 | 167 |

If this method picks a policy of Use, the new window will be 168 | opened, and decidePolicyForNavigationAction:request:frame:decisionListner: 169 | will be called with a WebNavigationType of WebNavigationTypeOther 170 | in its action. This is to avoid possible confusion about the modifiers. 171 | */ 172 | - (void)webView:(WebView *)webView decidePolicyForNewWindowAction:(NSDictionary *)actionInformation 173 | request:(NSURLRequest *)request 174 | newFrameName:(NSString *)frameName 175 | decisionListener:(id)listener; 176 | 177 | /*! 178 | @method webView:decidePolicyForMIMEType:request:frame: 179 | @discussion Returns the policy for content which has been partially loaded. 180 | Sent after webView:didStartProvisionalLoadForFrame: is sent on the WebFrameLoadDelegate. 181 | @param type MIME type for the resource. 182 | @param request A NSURLRequest for the partially loaded content. 183 | @param frame The frame which is loading the URL. 184 | @param listener The object to call when the decision is made 185 | */ 186 | - (void)webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type 187 | request:(NSURLRequest *)request 188 | frame:(WebFrame *)frame 189 | decisionListener:(id)listener; 190 | 191 | /*! 192 | @method webView:unableToImplementPolicy:error:forURL:inFrame: 193 | @discussion Called when a WebPolicy could not be implemented. It is up to the client to display appropriate feedback. 194 | @param policy The policy that could not be implemented. 195 | @param error The error that caused the policy to not be implemented. 196 | @param URL The URL of the resource for which a particular action was requested but failed. 197 | @param frame The frame in which the policy could not be implemented. 198 | */ 199 | - (void)webView:(WebView *)webView unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame; 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /extension/_BrowserPanelHooks.xm: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDMVersioning.h" 3 | #import "SDDownloadManager.h" 4 | 5 | #import "SDNavigationController.h" 6 | #import "SDDownloadListViewController.h" 7 | #import "SDDownloadPromptViewController.h" 8 | #import "SDFileBrowserNavigationController.h" 9 | 10 | #import "Safari/BrowserController.h" 11 | #import "Safari/TabController.h" 12 | #import "Safari/RotatablePopoverController.h" 13 | 14 | /* {{{ Private and Additional Categories */ 15 | @interface BrowserController (SDMAdditions) 16 | - (void)_setShowingDownloads:(BOOL)showing animate:(BOOL)animate; 17 | - (void)_presentModalViewController:(id)viewController fromButton:(UIView *)button; 18 | - (void)_presentModalViewControllerFromDownloadsButton:(id)x; 19 | - (void)toggleDownloadManagerFromButtonBar; 20 | - (void)_setBrowserPanel:(id)panel; 21 | - (void)setBrowserPanel:(id)panel; 22 | - (id)browserLayer; 23 | - (void)_forceDismissModalViewController:(BOOL)animate; 24 | @end 25 | 26 | @interface BrowserController (SafariFour) 27 | - (void)setCurrentPopoverController:(UIPopoverController *)p; 28 | @end 29 | 30 | @interface UIActionSheet (Private2) 31 | - (void)presentFromRect:(CGRect)rect inView:(UIView *)view direction:(NSInteger)direction allowInteractionWithViews:(NSArray *)views backgroundStyle:(NSInteger)backgroundStyle animated:(BOOL)animated; 32 | @end 33 | /* }}} */ 34 | 35 | %hook BrowserController 36 | - (id)_panelForPanelType:(int)type { 37 | %log; 38 | if(type == SDPanelTypeDownloadManager) { 39 | UIViewController *rootViewController = [[[SDDownloadListViewController alloc] init] autorelease]; 40 | return [[[SDNavigationController alloc] initWithRootViewController:rootViewController] autorelease]; 41 | } else if(type == SDPanelTypeDownloadPrompt) { 42 | SDDownloadRequest *req = [SDDownloadRequest pendingRequestForContext:[[self tabController] activeTabDocument]]; 43 | if(!req) 44 | req = [SDDownloadRequest pendingRequestForContext:MSHookIvar([self tabController], "_destinationTabDocument")]; 45 | if(!req) return nil; 46 | UIViewController *rootViewController = [[[SDDownloadPromptViewController alloc] initWithDownloadRequest:req delegate:[SDDownloadManager sharedManager]] autorelease]; 47 | return [[[SDNavigationController alloc] initWithRootViewController:rootViewController] autorelease]; 48 | } else if(type == SDPanelTypeFileBrowser) { 49 | SDDownloadRequest *req = [SDDownloadRequest pendingRequestForContext:[[self tabController] activeTabDocument]]; 50 | SDFileBrowserNavigationController *fileBrowser = [[[SDFileBrowserNavigationController alloc] initWithMode:SDFileBrowserModeImmediateDownload downloadRequest:req] autorelease]; 51 | fileBrowser.fileBrowserDelegate = [SDDownloadManager sharedManager]; 52 | return fileBrowser; 53 | } 54 | return %orig; 55 | } 56 | 57 | static void _setShowingCurrentPanel_animate_core(id self, BOOL showing, BOOL animate) { 58 | id panel = MSHookIvar(self, "_browserPanel"); 59 | if([panel panelType] == SDPanelTypeDownloadManager) { 60 | if(SDMSystemVersionGE(_SDM_iOS_3_2)) 61 | [MSHookIvar(self, "_browserView") resignFirstResponder]; 62 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) 63 | [self _resizeNavigationController:panel small:NO]; 64 | [self _setShowingDownloads:showing animate:animate]; 65 | } else if([panel panelType] == SDPanelTypeDownloadPrompt || [panel panelType] == SDPanelTypeFileBrowser) { 66 | UIViewController *presentationController = SDMSystemVersionLT(_SDM_iOS_5_0) ? [self _modalViewController] : MSHookIvar(self, "_rootViewController"); 67 | if(showing) { 68 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) 69 | [self _resizeNavigationController:panel small:NO]; 70 | [presentationController presentModalViewController:(UIViewController *)panel animated:animate]; 71 | //[self _presentModalViewController:panel fromRectInToolbar:CGRectZero]; 72 | } else { 73 | [self willHideBrowserPanel:panel]; 74 | [presentationController dismissModalViewControllerAnimated:animate]; 75 | //[self _forceDismissModalViewController:animate]; // 3.2+ 76 | } 77 | } 78 | } 79 | 80 | - (void)_setShowingCurrentPanel:(BOOL)showing animate:(BOOL)animate { 81 | %log; 82 | %orig; 83 | _setShowingCurrentPanel_animate_core(self, showing, animate); 84 | } 85 | 86 | %group Firmware_lt_32 87 | - (void)_setShowingCurrentPanel:(BOOL)showing { 88 | %log; 89 | %orig; 90 | _setShowingCurrentPanel_animate_core(self, showing, YES); 91 | } 92 | %end 93 | 94 | %group Firmware_ge_32 95 | %new(v@:) 96 | - (void)toggleDownloadManagerFromButtonBar { 97 | if([[self browserPanel] panelType] == SDPanelTypeDownloadManager) { 98 | [[self browserPanel] performSelector:@selector(close)]; 99 | } else { 100 | [self showBrowserPanelType:SDPanelTypeDownloadManager]; 101 | } 102 | } 103 | %end 104 | 105 | %group Firmware_lt_32 106 | %new(v@:) 107 | - (void)toggleDownloadManagerFromButtonBar { 108 | if([[self browserPanel] panelType] == SDPanelTypeDownloadManager) { 109 | [self hideBrowserPanelType:SDPanelTypeDownloadManager]; 110 | } else { 111 | [self showBrowserPanelType:SDPanelTypeDownloadManager]; 112 | } 113 | } 114 | %end 115 | 116 | %new(v@:ii) 117 | - (void)_setShowingDownloads:(BOOL)showing animate:(BOOL)animate { 118 | id controller = [self browserPanel]; 119 | if(showing) { 120 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) 121 | [self _resizeNavigationController:controller small:NO]; 122 | [self _presentModalViewControllerFromDownloadsButton:controller]; 123 | } else { 124 | [self willHideBrowserPanel:controller]; 125 | [self _forceDismissModalViewController:animate]; // 3.2+ 126 | } 127 | } 128 | 129 | %new(v@:@@) 130 | - (void)_presentModalViewController:(id)viewController fromButton:(UIView *)button { 131 | if(SDM$WildCat) { 132 | id rpc = [[%c(RotatablePopoverController) alloc] initWithContentViewController:viewController]; 133 | [rpc setPresentationRect:[button frame]]; 134 | [rpc setPresentationView:[self buttonBar]]; 135 | [rpc setPermittedArrowDirections:1]; 136 | [rpc setPassthroughViews:[NSArray arrayWithObject:[self buttonBar]]]; 137 | [rpc presentPopoverAnimated:NO]; 138 | [self setCurrentPopoverController:rpc]; 139 | [rpc release]; 140 | } else { 141 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) 142 | [[self _modalViewController] presentModalViewController:viewController animated:YES]; 143 | else 144 | [MSHookIvar(self, "_rootViewController") presentModalViewController:viewController animated:YES]; 145 | } 146 | } 147 | 148 | %group Firmware_lt_50 149 | %new(v@:@) 150 | - (void)_presentModalViewControllerFromDownloadsButton:(id)x { 151 | [self _presentModalViewController:x fromButton:objc_getAssociatedObject(self, kSDMAssociatedPortraitDownloadButton)]; 152 | } 153 | %end 154 | 155 | %group Firmware_ge_50 156 | %new(v@:@) 157 | - (void)_presentModalViewControllerFromDownloadsButton:(id)x { 158 | UIView *view = objc_getAssociatedObject(self, kSDMAssociatedPresentationViewKey); 159 | [self _presentModalViewController:x fromButton:view]; 160 | objc_setAssociatedObject(self, kSDMAssociatedPresentationViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 161 | } 162 | %end 163 | 164 | %group iPadHooks_Firmware_lt_50 165 | - (void)_presentModalViewControllerFromActionButton:(id)x { 166 | [self _presentModalViewController:x fromButton:objc_getAssociatedObject(self, kSDMAssociatedActionButton)]; 167 | } 168 | 169 | - (void)_presentModalViewControllerFromBookmarksButton:(id)x { 170 | [self _presentModalViewController:x fromButton:objc_getAssociatedObject(self, kSDMAssociatedBookmarksButton)]; 171 | } 172 | 173 | - (void)popupAlert:(UIActionSheet *)alert { 174 | [alert presentFromRect:[objc_getAssociatedObject(self, kSDMAssociatedActionButton) frame] inView:[self buttonBar] direction:1 allowInteractionWithViews:[NSArray arrayWithObjects:[self buttonBar], nil] backgroundStyle:0 animated:YES]; 175 | } 176 | %end 177 | 178 | - (BOOL)showBrowserPanelType:(int)arg1 { 179 | %log; 180 | if(arg1 == 5 && [[self browserPanel] panelType] == SDPanelTypeDownloadManager) { 181 | [self hideBrowserPanelType:SDPanelTypeDownloadManager]; 182 | } 183 | BOOL x = %orig; 184 | return x; 185 | } 186 | 187 | - (BOOL)hideBrowserPanelType:(int)arg1 { 188 | %log; 189 | if(arg1 == SDPanelTypeDownloadManager) { 190 | [self _setShowingDownloads:NO animate:YES]; 191 | return YES; 192 | } 193 | BOOL x = %orig; 194 | NSLog(@"------- hideBrowserPanelType: %d", arg1); 195 | return x; 196 | } 197 | 198 | %group Firmware_lt_32 199 | %new(v@:i) // Missing on < 4.0 200 | - (void)_forceDismissModalViewController:(BOOL)animated { 201 | [self _forceDismissModalViewController]; 202 | } 203 | %end 204 | 205 | %group Firmware_ge_32 206 | %new(@@:) 207 | - (id)browserLayer { 208 | return MSHookIvar(self, "_transitionView"); 209 | } 210 | %end 211 | %end 212 | 213 | %group Firmware_lt_50 214 | %hook Application 215 | - (void)applicationWillSuspend { 216 | BrowserController *sbc = [%c(BrowserController) sharedBrowserController]; 217 | if([[sbc browserPanel] panelType] == SDPanelTypeDownloadManager) { 218 | [sbc hideBrowserPanelType:SDPanelTypeDownloadManager]; 219 | [sbc _setShowingDownloads:NO animate:YES]; 220 | } 221 | %orig; 222 | } 223 | %end 224 | %end 225 | 226 | void _init_browserPanel() { 227 | %init; 228 | if(SDMSystemVersionLT(_SDM_iOS_3_2)) 229 | %init(Firmware_lt_32); 230 | else 231 | %init(Firmware_ge_32); 232 | 233 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) { 234 | %init(Firmware_lt_50); 235 | } else { 236 | %init(Firmware_ge_50); 237 | } 238 | 239 | if(SDM$WildCat) { 240 | if(SDMSystemVersionLT(_SDM_iOS_5_0)) { 241 | %init(iPadHooks_Firmware_lt_50); 242 | } 243 | } 244 | } 245 | 246 | // vim:filetype=logos:sw=8:ts=8:noet 247 | -------------------------------------------------------------------------------- /extension/_CustomToolbar_Old.xm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SDMCommon.h" 3 | #import "SDMVersioning.h" 4 | 5 | #import "Safari/BrowserController.h" 6 | #import "Safari/Application.h" 7 | #import "SDDownloadManager.h" 8 | #import "UIKitExtra/UIToolbarButton.h" 9 | 10 | NSString * const kSDMAssociatedPortraitDownloadButton = @"kSDMAssociatedPortraitDownloadButton"; 11 | NSString * const kSDMAssociatedLandscapeDownloadButton = @"kSDMAssociatedLandscapeDownloadButton"; 12 | NSString * const kSDMAssociatedActionButton = @"kSDMAssociatedActionButton"; 13 | NSString * const kSDMAssociatedBookmarksButton = @"kSDMAssociatedBookmarksButton"; 14 | 15 | @interface BrowserController (SDMAdditions) 16 | - (void)toggleDownloadManagerFromButtonBar; 17 | @end 18 | 19 | @interface BrowserButtonBar (Private) 20 | - (NSArray *)buttonItems; 21 | - (void)setButtonItems:(NSArray *)buttonItems; 22 | - (void)registerButtonGroup:(int)group withButtons:(int *)buttons withCount:(int)count; 23 | - (void)showButtonGroup:(int)group withDuration:(NSTimeInterval)duration; 24 | @end 25 | 26 | static NSString *portraitIconFilename(void) { 27 | NSString *name = @"Download"; 28 | if(SDM$WildCat) return @"DownloadT.png"; 29 | return SDMSystemVersionGE(_SDM_iOS_4_0) ? name : [name stringByAppendingString:@".png"]; 30 | } 31 | 32 | static NSString *landscapeIconFilename(void) { 33 | NSString *name = @"DownloadSmall"; 34 | return SDMSystemVersionGE(_SDM_iOS_4_0) ? name : [name stringByAppendingString:@".png"]; 35 | } 36 | 37 | static void initCustomToolbar(void) { 38 | BrowserController *bcont = [SDM$BrowserController sharedBrowserController]; 39 | BrowserButtonBar *buttonBar = MSHookIvar(bcont, "_buttonBar"); 40 | CFMutableDictionaryRef _groups = MSHookIvar(buttonBar, "_groups"); 41 | int cg = MSHookIvar(buttonBar, "_currentButtonGroup"); 42 | NSArray *_buttonItems = [buttonBar buttonItems]; 43 | 44 | NSMutableArray *mutButtonItems = [_buttonItems mutableCopy]; 45 | 46 | id x = [%c(BrowserButtonBar) imageButtonItemWithName:portraitIconFilename() 47 | tag:61 48 | action:@selector(toggleDownloadManagerFromButtonBar) 49 | target:[NSValue valueWithNonretainedObject:[SDM$BrowserController sharedBrowserController]]]; 50 | 51 | [mutButtonItems addObject:x]; 52 | CFDictionaryRemoveValue(_groups, (void*)1); 53 | 54 | if(!SDM$WildCat) { 55 | // Landscape (non-iPad) 56 | id y = [%c(BrowserButtonBar) imageButtonItemWithName:landscapeIconFilename() 57 | tag:62 58 | action:@selector(toggleDownloadManagerFromButtonBar) 59 | target:[NSValue valueWithNonretainedObject:[SDM$BrowserController sharedBrowserController]]]; 60 | [mutButtonItems addObject:y]; 61 | CFDictionaryRemoveValue(_groups, (void*)2); 62 | } 63 | 64 | 65 | [buttonBar setButtonItems:mutButtonItems]; 66 | [mutButtonItems release]; 67 | 68 | int portraitGroup[] = {5, 7, 15, 1, 61, 3}; 69 | int landscapeGroup[] = {6, 8, 16, 2, 62, 4}; 70 | 71 | if(SDM$WildCat) { // The iPad has a different button order than the iPhone. 72 | portraitGroup[0] = 5; 73 | portraitGroup[1] = 7; 74 | portraitGroup[2] = 3; 75 | portraitGroup[3] = 1; 76 | portraitGroup[4] = 15; 77 | portraitGroup[5] = 61; 78 | } 79 | 80 | [buttonBar registerButtonGroup:1 81 | withButtons:portraitGroup 82 | withCount:6]; 83 | if(!SDM$WildCat) { 84 | [buttonBar registerButtonGroup:2 85 | withButtons:landscapeGroup 86 | withCount:6]; 87 | } 88 | 89 | if (cg == 1 || cg == 2) 90 | [buttonBar showButtonGroup:cg 91 | withDuration:0]; 92 | } 93 | 94 | %hook Application 95 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 96 | %orig; 97 | initCustomToolbar(); 98 | } 99 | %end 100 | 101 | %hook BrowserButtonBar 102 | - (void)positionButtons:(NSArray *)buttons tags:(int *)tags count:(int)count group:(int)group { 103 | %orig; 104 | 105 | if(group != 1 && group != 2) { 106 | return; 107 | } 108 | 109 | NSLog(@"Button array: %@", [buttons description]); 110 | float maxWidth = self.frame.size.width; 111 | float buttonBoxWidth = floorf(maxWidth / count); 112 | if((int)buttonBoxWidth % 2 == 1) buttonBoxWidth -= 1.0f; 113 | float curX = 0; 114 | float maxX = buttonBoxWidth; 115 | int curButton = 0; 116 | float YOrigin = SDM$WildCat ? 10 : 2; 117 | for(UIToolbarButton *button in buttons) { 118 | curX = curButton * buttonBoxWidth; 119 | maxX = curX + buttonBoxWidth; 120 | float curWidth = button.frame.size.width; 121 | float curHeight = button.frame.size.height; 122 | float newXOrigin = floorf(maxX - (buttonBoxWidth / 2.0) - (curWidth / 2.0)); 123 | [button setFrame:CGRectMake(newXOrigin, YOrigin, curWidth, curHeight)]; 124 | 125 | int tag = button.tag; 126 | if(tag == 61) 127 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedPortraitDownloadButton, button, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 128 | else if(tag == 62) 129 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedLandscapeDownloadButton, button, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 130 | else if(tag == 1) 131 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedBookmarksButton, button, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 132 | else if(tag == 15) 133 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedActionButton, button, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 134 | 135 | curButton++; 136 | } 137 | return; 138 | } 139 | %end 140 | 141 | %hook BrowserController 142 | %new(v@:@) 143 | - (void)_sdmUpdateBadge:(NSString *)value { 144 | UIToolbarButton *portraitButton = objc_getAssociatedObject(self, kSDMAssociatedPortraitDownloadButton); 145 | UIToolbarButton *landscapeButton = objc_getAssociatedObject(self, kSDMAssociatedLandscapeDownloadButton); 146 | [portraitButton _setBadgeValue:value]; 147 | [landscapeButton _setBadgeValue:value]; 148 | } 149 | %end 150 | 151 | void _init_customToolbar_legacy() { 152 | %init; 153 | } 154 | 155 | // vim:filetype=logos:ts=8:sw=8:noet 156 | -------------------------------------------------------------------------------- /extension/_CustomToolbar_iOS5.xm: -------------------------------------------------------------------------------- 1 | #import "Safari/RotatablePopoverController.h" 2 | 3 | #import 4 | #import "SDMCommon.h" 5 | #import "SDResources.h" 6 | 7 | #import "Safari/BrowserController.h" 8 | #import "UIKitExtra/UIToolbarButton.h" 9 | #import "SDDownloadButtonItem.h" 10 | 11 | @interface BrowserToolbar: UIToolbar 12 | - (SDDownloadButtonItem *)_downloadButtonItem; 13 | @end 14 | 15 | @interface BrowserController (SDMAdditions) 16 | - (void)toggleDownloadManagerFromButtonBar; 17 | - (BrowserToolbar *)buttonBar; 18 | @end 19 | 20 | @interface UIBarButtonItem (SDMPrivate) 21 | - (UIToolbarButton *)view; 22 | @end 23 | 24 | %subclass SDDownloadButtonItem: SpacedBarButtonItem 25 | static NSString * const kSDDownloadButtonItemAssociatedBadgeKey = @"kSDDownloadButtonItemAssociatedBadgeKey"; 26 | %new(v@:@) 27 | - (void)setBadge:(NSString *)badge { 28 | objc_setAssociatedObject(self, kSDDownloadButtonItemAssociatedBadgeKey, badge, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 29 | [(UIToolbarButton *)[self view] _setBadgeValue:badge]; 30 | } 31 | 32 | %new(@@:) 33 | - (NSString *)badge { 34 | return objc_getAssociatedObject(self, kSDDownloadButtonItemAssociatedBadgeKey); 35 | } 36 | %end 37 | 38 | %hook SpacedBarButtonItem 39 | - (id)init { 40 | if((self = %orig) != nil) { 41 | UIBarButtonItem *&_precedingFixedSpace = MSHookIvar(self, "_precedingFixedSpace"); 42 | [_precedingFixedSpace release]; 43 | _precedingFixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; 44 | } return self; 45 | } 46 | 47 | %new(v@:) 48 | - (void)destroyPrecedingSpace { 49 | UIBarButtonItem *&_precedingFixedSpace = MSHookIvar(self, "_precedingFixedSpace"); 50 | [_precedingFixedSpace release]; 51 | _precedingFixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; 52 | [_precedingFixedSpace setWidth:0.f]; 53 | } 54 | %end 55 | 56 | %hook BrowserToolbar 57 | NSString * const kSDMAssociatedPresentationViewKey = @"kSDMAssociatedPresentationViewKey"; 58 | 59 | // Private 60 | static NSString * const kSDMAssociatedButtonDictionaryKey = @"kSDMAssociatedButtonDictionaryKey"; 61 | - (NSMutableArray *)_defaultSpacedItems { 62 | NSMutableArray *orig = [%orig mutableCopy]; 63 | SpacedBarButtonItem *_tabExposeItem = MSHookIvar(self, "_tabExposeItem"); 64 | 65 | UIBarButtonItem *downloadButtonItem = [[%c(SDDownloadButtonItem) alloc] init]; 66 | downloadButtonItem.image = [SDResources imageNamed:@"DownloadButton"]; 67 | downloadButtonItem.landscapeImagePhone = [SDResources imageNamed:@"DownloadButtonSmall"]; 68 | downloadButtonItem.style = UIBarButtonItemStylePlain; 69 | downloadButtonItem.target = [SDM$BrowserController sharedBrowserController]; 70 | downloadButtonItem.action = @selector(_downloadManagerButtonShim:); 71 | if(SDM$WildCat) 72 | [orig addObject:downloadButtonItem]; 73 | else 74 | [orig insertObject:downloadButtonItem atIndex:[orig indexOfObjectIdenticalTo:_tabExposeItem]]; 75 | [downloadButtonItem release]; 76 | [[orig objectAtIndex:0] destroyPrecedingSpace]; 77 | 78 | objc_setAssociatedObject(self, kSDMAssociatedButtonDictionaryKey, [NSMutableDictionary dictionary], OBJC_ASSOCIATION_RETAIN_NONATOMIC); 79 | return [orig autorelease]; 80 | } 81 | 82 | // Make this non-operational. 83 | - (void)_updateFixedSpacing { 84 | return; 85 | } 86 | 87 | %new(@@:) 88 | - (SDDownloadButtonItem *)_downloadButtonItem { 89 | NSArray *items = [self items]; 90 | NSInteger index = [items indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) { 91 | if([obj isKindOfClass:%c(SDDownloadButtonItem)]) { 92 | *stop = YES; 93 | return YES; 94 | } 95 | return NO; 96 | }]; 97 | return index != NSNotFound ? [items objectAtIndex:index] : nil; 98 | } 99 | 100 | - (void)layoutSubviews { 101 | SDDownloadButtonItem *downloadButton = [self _downloadButtonItem]; 102 | NSLog(@"Setting badge %@ on view %@", [downloadButton badge], [downloadButton view]); 103 | [[downloadButton view] _setBadgeValue:[downloadButton badge]]; 104 | 105 | NSMutableDictionary *buttonDictionary = objc_getAssociatedObject(self, kSDMAssociatedButtonDictionaryKey); 106 | UIBarButtonItem *_backItem = MSHookIvar(self, "_backItem"); 107 | UIBarButtonItem *_forwardItem = MSHookIvar(self, "_forwardItem"); 108 | UIBarButtonItem *_actionItem = MSHookIvar(self, "_actionItem"); 109 | UIBarButtonItem *_bookmarksItem = MSHookIvar(self, "_bookmarksItem"); 110 | 111 | [buttonDictionary setObject:[_backItem view] forKey:@"_backItem"]; 112 | [buttonDictionary setObject:[_forwardItem view] forKey:@"_forwardItem"]; 113 | [buttonDictionary setObject:[_actionItem view] forKey:@"_actionItem"]; 114 | [buttonDictionary setObject:[_bookmarksItem view] forKey:@"_bookmarksItem"]; 115 | 116 | %orig; 117 | } 118 | %end 119 | 120 | %hook BrowserController 121 | %new(v@:@) 122 | - (void)_downloadManagerButtonShim:(UIBarButtonItem *)sender { 123 | objc_setAssociatedObject([SDM$BrowserController sharedBrowserController], kSDMAssociatedPresentationViewKey, [(UIBarButtonItem *)sender view], OBJC_ASSOCIATION_RETAIN_NONATOMIC); 124 | [self toggleDownloadManagerFromButtonBar]; 125 | } 126 | 127 | %new(v@:@) 128 | - (void)_sdmUpdateBadge:(NSString *)value { 129 | SDDownloadButtonItem *downloadButton = [[self buttonBar] _downloadButtonItem]; 130 | [downloadButton setBadge:value]; 131 | } 132 | %end 133 | 134 | %group iPadHooks 135 | %hook BrowserToolbar 136 | - (CGRect)actionPopoverPresentationRect { 137 | NSMutableDictionary *buttonDictionary = objc_getAssociatedObject(self, kSDMAssociatedButtonDictionaryKey); 138 | return [(UIView *)[buttonDictionary objectForKey:@"_actionItem"] frame]; 139 | } 140 | 141 | - (CGRect)backPopoverPresentationRect { 142 | NSMutableDictionary *buttonDictionary = objc_getAssociatedObject(self, kSDMAssociatedButtonDictionaryKey); 143 | return [(UIView *)[buttonDictionary objectForKey:@"_backItem"] frame]; 144 | } 145 | 146 | - (CGRect)bookmarksPopoverPresentationRect { 147 | NSMutableDictionary *buttonDictionary = objc_getAssociatedObject(self, kSDMAssociatedButtonDictionaryKey); 148 | return [(UIView *)[buttonDictionary objectForKey:@"_bookmarksItem"] frame]; 149 | } 150 | 151 | - (CGRect)forwardPopoverPresentationRect { 152 | NSMutableDictionary *buttonDictionary = objc_getAssociatedObject(self, kSDMAssociatedButtonDictionaryKey); 153 | return [(UIView *)[buttonDictionary objectForKey:@"_forwardItem"] frame]; 154 | } 155 | %end 156 | %end 157 | 158 | void _init_customToolbar() { 159 | %init; 160 | if(SDM$WildCat) { 161 | %init(iPadHooks); 162 | } 163 | } 164 | 165 | // vim:filetype=logos:sw=8:ts=8:noet 166 | -------------------------------------------------------------------------------- /extension/_Interaction_iOS3.xm: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDMVersioning.h" 3 | #import "SDDownloadManager.h" 4 | #import "common/SDResources.h" 5 | 6 | #import "UIKitExtra/UIWebDocumentView.h" 7 | #import "UIKitExtra/UIWebViewWebViewDelegate.h" 8 | 9 | #import "Safari/BrowserController.h" 10 | #import "Safari/TabController.h" 11 | 12 | @interface UIActionSheet (SDMPrivate) 13 | - (NSMutableArray *)buttons; 14 | @end 15 | 16 | /* {{{ struct interaction on 3.2+ */ 17 | struct interaction32 { 18 | NSTimer *timer; 19 | struct CGPoint location; 20 | char isBlocked; 21 | char isCancelled; 22 | char isOnWebThread; 23 | char isDisplayingHighlight; 24 | char attemptedClick; 25 | struct CGPoint lastPanTranslation; 26 | DOMNode *element; 27 | char defersCallbacksState; 28 | UIInformalDelegate *delegate; 29 | int interactionSheetType; 30 | UIActionSheet *interactionSheet; 31 | char allowsImageSheet; 32 | char allowsDataDetectorsSheet; 33 | } _interaction32; 34 | /* }}} */ 35 | 36 | /* {{{ struct interaction on < 3.2 */ 37 | struct interactionnot32 { 38 | NSTimer* timer; 39 | CGPoint location; 40 | BOOL isBlocked; 41 | BOOL isCancelled; 42 | BOOL isOnWebThread; 43 | BOOL isDisplayingHighlight; 44 | BOOL attemptedClick; 45 | BOOL isGestureScrolling; 46 | CGPoint gestureScrollPoint; 47 | CGPoint gestureCurrentPoint; 48 | BOOL hasAttemptedGestureScrolling; 49 | UIView* candidate; 50 | BOOL forwardingGuard; 51 | SEL mouseUpForwarder; 52 | SEL mouseDraggedForwarder; 53 | DOMNode *element; 54 | BOOL defersCallbacksState; 55 | UIInformalDelegate* delegate; 56 | int interactionSheetType; 57 | UIActionSheet* interactionSheet; 58 | BOOL allowsImageSheet; 59 | BOOL allowsDataDetectorsSheet; 60 | struct { 61 | BOOL active; 62 | BOOL defaultPrevented; 63 | NSMutableArray* regions; 64 | } directEvents; 65 | }; 66 | /* }}} */ 67 | 68 | static NSURL *interactionURL = nil; 69 | 70 | @interface DOMNode : NSObject 71 | -(DOMNode*)parentNode; 72 | -(NSURL*)absoluteLinkURL; 73 | -(NSURL*)absoluteImageURL; 74 | @end 75 | 76 | %hook UIWebDocumentView 77 | - (void)actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(int)index { 78 | if(index == 1336) { 79 | if(interactionURL) { 80 | NSURLRequest *request = [NSURLRequest requestWithURL:interactionURL]; 81 | [[SDDownloadManager sharedManager] downloadRequestForImmediateURLRequest:request context:[[[SDM$BrowserController sharedBrowserController] tabController] activeTabDocument]]; 82 | [[SDM$BrowserController sharedBrowserController] showBrowserPanelType:SDPanelTypeFileBrowser]; 83 | } 84 | } 85 | %orig; 86 | [interactionURL release]; 87 | interactionURL = nil; 88 | } 89 | 90 | static void showBrowserSheetHookInternals(UIWebDocumentView *self, UIActionSheet *sheet, DOMNode *&domElement) { 91 | NSLog(@"DOM Element is %@", domElement); 92 | NSMutableArray *buttons = [sheet buttons]; 93 | NSString *actionTitle = nil; 94 | id myButton; 95 | 96 | DOMNode *anchorNode = domElement; 97 | while(anchorNode && ![anchorNode isKindOfClass:%c(DOMHTMLAnchorElement)]) { 98 | NSLog(@"not htmlanchorelement oh no %@", anchorNode); 99 | anchorNode = [anchorNode parentNode]; 100 | } 101 | 102 | if(anchorNode) { 103 | NSLog(@"100%% certainty that this is an anchor node. %@", anchorNode); 104 | domElement = anchorNode; 105 | } else { 106 | NSLog(@"There's definitely not an anchor node here."); 107 | } 108 | 109 | if([domElement isKindOfClass:%c(DOMHTMLAnchorElement)]) { 110 | NSLog(@"htmlanchorelement yay %@", domElement); 111 | interactionURL = [[domElement absoluteLinkURL] copy]; 112 | actionTitle = SDLocalizedString(@"DOWNLOAD_TARGET"); 113 | } else if([domElement isKindOfClass:%c(DOMHTMLImageElement)]) { 114 | NSLog(@"htmlimageelement yay %@", domElement); 115 | interactionURL = [[domElement absoluteImageURL] copy]; 116 | actionTitle = SDLocalizedString(@"DOWNLOAD_IMAGE"); 117 | } else { 118 | interactionURL = nil; 119 | } 120 | 121 | if(interactionURL) { 122 | NSString *scheme = [interactionURL scheme]; 123 | NSLog(@"url is %@", interactionURL); 124 | if([scheme isEqualToString:@"http"] 125 | || [scheme isEqualToString:@"https"] 126 | || [scheme isEqualToString:@"ftp"]) { 127 | [sheet addButtonWithTitle:actionTitle]; 128 | myButton = [buttons lastObject]; 129 | [myButton retain]; 130 | [myButton setTag:1337]; 131 | [buttons removeObject:myButton]; 132 | [buttons insertObject:myButton atIndex:0]; 133 | [myButton release]; 134 | [sheet setDestructiveButtonIndex:0]; 135 | [sheet setCancelButtonIndex:([buttons count] - 1)]; 136 | } else { 137 | [interactionURL release]; 138 | interactionURL = nil; 139 | } 140 | } 141 | } 142 | 143 | %group Firmware_ge_32 144 | - (void)showBrowserSheet:(id)sheet atPoint:(CGPoint)p { 145 | %log; 146 | struct interaction32 i = MSHookIvar(self, "_interaction"); 147 | showBrowserSheetHookInternals(self, sheet, i.element); 148 | %orig; 149 | } 150 | %end 151 | 152 | %group Firmware_lt_32 153 | - (void)showBrowserSheet:(id)sheet { 154 | %log; 155 | struct interactionnot32 i = MSHookIvar(self, "_interaction"); 156 | showBrowserSheetHookInternals(self, sheet, i.element); 157 | %orig; 158 | } 159 | %end 160 | %end 161 | 162 | void _init_interaction_legacy() { 163 | %init; 164 | if(SDMSystemVersionLT(_SDM_iOS_3_2)) 165 | %init(Firmware_lt_32); 166 | else 167 | %init(Firmware_ge_32); 168 | } 169 | // vim:filetype=logos:ts=8:sw=8:noexpandtab 170 | -------------------------------------------------------------------------------- /extension/_Interaction_iOS5.xm: -------------------------------------------------------------------------------- 1 | #import "SDMCommon.h" 2 | #import "SDMVersioning.h" 3 | 4 | #import "UIKitExtra/UIWebDocumentView.h" 5 | #import "UIKitExtra/UIWebViewWebViewDelegate.h" 6 | #import "UIWebElementAction.h" 7 | 8 | #import "SDResources.h" 9 | #import "SDDownloadManager.h" 10 | 11 | #import "Safari/BrowserController.h" 12 | #import "Safari/TabController.h" 13 | 14 | @interface DOMNode: NSObject 15 | - (DOMNode *)parentNode; 16 | - (NSURL *)absoluteImageURL; 17 | @end 18 | 19 | %hook BrowserController 20 | - (NSMutableArray *)_actionsForElement:(DOMNode *)domElement withTargetURL:(NSURL *)url suggestedActions:(NSArray *)suggestedActions { 21 | NSMutableArray *actions = %orig; 22 | 23 | NSMutableArray *downloadActions = [NSMutableArray array]; 24 | if(url) { 25 | NSString *scheme = [url scheme]; 26 | if([scheme hasPrefix:@"http"] 27 | || [scheme isEqualToString:@"ftp"]) { 28 | [downloadActions addObject:[%c(UIWebElementAction) customElementActionWithTitle:SDLocalizedString(@"DOWNLOAD_TARGET") actionHandler:^{ 29 | NSURLRequest *request = [NSURLRequest requestWithURL:url]; 30 | [[SDDownloadManager sharedManager] downloadRequestForImmediateURLRequest:request context:[[self tabController] activeTabDocument]]; 31 | [self showBrowserPanelType:SDPanelTypeFileBrowser]; 32 | }]]; 33 | } 34 | } 35 | if([domElement isKindOfClass:%c(DOMHTMLImageElement)]) { 36 | [downloadActions addObject:[%c(UIWebElementAction) customElementActionWithTitle:SDLocalizedString(@"DOWNLOAD_IMAGE") actionHandler:^{ 37 | NSURL *url = [domElement absoluteImageURL]; 38 | NSURLRequest *request = [NSURLRequest requestWithURL:url]; 39 | [[SDDownloadManager sharedManager] downloadRequestForImmediateURLRequest:request context:[[self tabController] activeTabDocument]]; 40 | [self showBrowserPanelType:SDPanelTypeFileBrowser]; 41 | }]]; 42 | } 43 | 44 | [actions insertObjects:downloadActions atIndexes:[NSIndexSet indexSetWithIndexesInRange:(NSRange){0, downloadActions.count}]]; 45 | return actions; 46 | } 47 | %end 48 | 49 | void _init_interaction() { 50 | %init; 51 | } 52 | -------------------------------------------------------------------------------- /extension/_WebPolicyDelegateHooks.xm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SDMCommon.h" 3 | #import "SDDownloadManager.h" 4 | 5 | //#import "Safari/BrowserController.h" 6 | //#import "Safari/PageLoad.h" 7 | #import "Safari/TabDocument.h" 8 | #import "WebPolicyDelegate.h" 9 | 10 | %hook TabDocument 11 | - (void)webView:(WebView *)view 12 | decidePolicyForNewWindowAction:(NSDictionary *)action 13 | request:(NSURLRequest *)request 14 | newFrameName:(NSString *)newFrameName 15 | decisionListener:(id)decisionListener { 16 | %log; 17 | SDDownloadManager* downloader = [SDDownloadManager sharedManager]; 18 | 19 | BOOL load = [downloader webView:view decideAction:action forRequest:request withMimeType:nil inFrame:nil withListener:decisionListener context:self]; 20 | 21 | if(load) { 22 | NSLog(@"WDW: not handled"); 23 | %orig; 24 | } else { 25 | NSLog(@"WDW: handled"); 26 | [decisionListener ignore]; 27 | [self _setLoading:NO withError:nil]; 28 | } 29 | NSLog(@"#####################################################"); 30 | } 31 | 32 | - (void)webView:(WebView *)view 33 | decidePolicyForNavigationAction:(NSDictionary *)action 34 | request:(NSURLRequest *)request 35 | frame:(WebFrame *)frame 36 | decisionListener:(id)decisionListener { 37 | %log; 38 | 39 | SDDownloadManager* downloader = [SDDownloadManager sharedManager]; 40 | 41 | BOOL load = [downloader webView:view decideAction:action forRequest:request withMimeType:nil inFrame:frame withListener:decisionListener context:self]; 42 | 43 | if(load) { 44 | NSLog(@"NAV: not handled"); 45 | %orig; 46 | } else { 47 | NSLog(@"NAV: handled"); 48 | [decisionListener ignore]; 49 | [self _setLoading:NO withError:nil]; 50 | } 51 | NSLog(@"#####################################################"); 52 | } 53 | 54 | - (void)webView:(WebView *)view 55 | decidePolicyForMIMEType:(NSString *)type 56 | request:(NSURLRequest *)request 57 | frame:(WebFrame *)frame 58 | decisionListener:(id)decisionListener { 59 | %log; 60 | 61 | SDDownloadManager* downloader = [SDDownloadManager sharedManager]; 62 | 63 | BOOL load = [downloader webView:view decideAction:nil forRequest:request withMimeType:type inFrame:frame withListener:decisionListener context:self]; 64 | 65 | if(load) { 66 | NSLog(@"MIME: not handled"); 67 | %orig; 68 | } else { 69 | NSLog(@"MIME: handled"); 70 | [decisionListener ignore]; 71 | [self _setLoading:NO withError:nil]; 72 | } 73 | NSLog(@"#####################################################"); 74 | } 75 | %end 76 | 77 | void _init_webPolicyDelegate() { 78 | %init; 79 | } 80 | 81 | // vim:filetype=logos:sw=8:ts=8:noet 82 | -------------------------------------------------------------------------------- /layout/Applications/MobileSafari.app/Download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Applications/MobileSafari.app/Download.png -------------------------------------------------------------------------------- /layout/Applications/MobileSafari.app/Download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Applications/MobileSafari.app/Download@2x.png -------------------------------------------------------------------------------- /layout/Applications/MobileSafari.app/DownloadSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Applications/MobileSafari.app/DownloadSmall.png -------------------------------------------------------------------------------- /layout/Applications/MobileSafari.app/DownloadSmall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Applications/MobileSafari.app/DownloadSmall@2x.png -------------------------------------------------------------------------------- /layout/Applications/MobileSafari.app/DownloadT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Applications/MobileSafari.app/DownloadT.png -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: net.howett.safaridownloader 2 | Name: Safari Download Manager 3 | Pre-Depends: firmware (>= 3.0) 4 | Depends: mobilesubstrate, preferenceloader, co.cocoanuts.sandcastle (>> 2.0-1), net.howett.safaridownloader-lproj 5 | Conflicts: net.howett.downloader, com.bigboss.patchsafari2 (<= 2.0.2587-1) 6 | Replaces: net.howett.downloader 7 | Version: 2.1 8 | Architecture: iphoneos-arm 9 | Description: A full download manager for Safari. 10 | Maintainer: Dustin Howett 11 | Author: Dustin Howett 12 | Section: Networking 13 | Depiction: http://howett.net/cydia/depict/sdm/index.html 14 | Homepage: http://howett.net/cydia/depict/sdm/index.html 15 | Sponsor: thebigboss.org 16 | dev: dustinhowett 17 | tag: cydia::commercial 18 | -------------------------------------------------------------------------------- /layout/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ $1 == configure ]]; then 3 | if [[ -e /var/mobile/Media/Downloads ]]; then 4 | chown -R mobile.mobile /var/mobile/Media/Downloads 5 | else 6 | mkdir -p /var/mobile/Media/Downloads 7 | chown -R mobile.mobile /var/mobile/Media/Downloads 8 | fi 9 | fi 10 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButton.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButton@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButton@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButtonSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButtonSmall.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButtonSmall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/DownloadButtonSmall@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Archives.png: -------------------------------------------------------------------------------- 1 | package-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Archives@2x.png: -------------------------------------------------------------------------------- 1 | package-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Documents.png: -------------------------------------------------------------------------------- 1 | x-office-document.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Documents@2x.png: -------------------------------------------------------------------------------- 1 | x-office-document@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Images.png: -------------------------------------------------------------------------------- 1 | image-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Images@2x.png: -------------------------------------------------------------------------------- 1 | image-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Media.png: -------------------------------------------------------------------------------- 1 | audio-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Media@2x.png: -------------------------------------------------------------------------------- 1 | audio-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Miscellaneous.png: -------------------------------------------------------------------------------- 1 | application-x-executable.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/Category-Miscellaneous@2x.png: -------------------------------------------------------------------------------- 1 | application-x-executable@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-pdf.png: -------------------------------------------------------------------------------- 1 | x-office-document.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-pdf@2x.png: -------------------------------------------------------------------------------- 1 | x-office-document@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-bittorrent.png: -------------------------------------------------------------------------------- 1 | text-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-bittorrent@2x.png: -------------------------------------------------------------------------------- 1 | text-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-executable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-executable.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-executable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-x-executable@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-xml.png: -------------------------------------------------------------------------------- 1 | text-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/application-xml@2x.png: -------------------------------------------------------------------------------- 1 | text-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/audio-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/audio-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/audio-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/audio-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/disk-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/disk-image.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/disk-image@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/disk-image@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/folder.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/folder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/folder@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/font-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/font-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/font-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/font-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/home.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/home@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/image-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/image-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/image-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/image-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/package-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/package-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/package-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/package-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-plain.png: -------------------------------------------------------------------------------- 1 | text-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-plain@2x.png: -------------------------------------------------------------------------------- 1 | text-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-script.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-script@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/text-x-script@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/unknown.png: -------------------------------------------------------------------------------- 1 | text-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/unknown@2x.png: -------------------------------------------------------------------------------- 1 | text-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/video-x-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/video-x-generic.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/video-x-generic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/video-x-generic@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-address-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-address-book.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-address-book@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-address-book@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-calendar.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-calendar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-calendar@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-document.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-document@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-document@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-drawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-drawing.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-drawing@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-drawing@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-presentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-presentation.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-presentation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-presentation@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-spreadsheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-spreadsheet.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-spreadsheet@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DHowett/safaridownloader/b8b6882abf3fba79fed6f377310cd5c721af597c/layout/Library/Application Support/Safari Downloader/Images.bundle/Icons/x-office-spreadsheet@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Images.bundle/Info.plist: -------------------------------------------------------------------------------- 1 | { 2 | CFBundleDevelopmentRegion = English; 3 | CFBundleIdentifier = net.howett.safaridownloader.images; 4 | CFBundleInfoDictionaryVersion = 6.0; 5 | CFBundlePackageType = BNDL; 6 | CFBundleShortVersionString = 1.0; 7 | CFBundleSignature = "????"; 8 | CFBundleVersion = 1.0; 9 | DTPlatformName = iphoneos; 10 | MinimumOSVersion = 3.0; 11 | } 12 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/Info.plist: -------------------------------------------------------------------------------- 1 | { 2 | CFBundleDevelopmentRegion = English; 3 | CFBundleIdentifier = net.howett.safaridownloader; 4 | CFBundleInfoDictionaryVersion = 6.0; 5 | CFBundlePackageType = BNDL; 6 | CFBundleShortVersionString = 1.0; 7 | CFBundleSignature = "????"; 8 | CFBundleVersion = 1.0; 9 | DTPlatformName = iphoneos; 10 | MinimumOSVersion = 3.0; 11 | } 12 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/en.lproj/FileTypes.strings: -------------------------------------------------------------------------------- 1 | // Categories 2 | "Archives" = "Archives"; 3 | "Documents" = "Documents"; 4 | "Images" = "Images"; 5 | "Media" = "Media"; 6 | "Miscellaneous" = "Miscellaneous"; 7 | 8 | // Types 9 | "PNG image" = "PNG image"; 10 | "ZIP archive" = "ZIP archive"; 11 | "Apple Disk Image" = "Apple Disk Image"; 12 | "3GPP video" = "3GPP video"; 13 | "PDF document" = "PDF document"; 14 | "Android package" = "Android package"; 15 | "Tape archive" = "Tape archive"; 16 | "JPEG image" = "JPEG image"; 17 | "WAVE audio" = "WAVE audio"; 18 | "bzip2 Archive" = "bzip2 Archive"; 19 | "AVI video" = "AVI video"; 20 | "Microsoft PowerPoint Document" = "Microsoft PowerPoint Document"; 21 | "TIFF image" = "TIFF image"; 22 | "OGG" = "OGG"; 23 | "WMA" = "WMA"; 24 | "iCalendar" = "iCalendar"; 25 | "Perl script" = "Perl script"; 26 | "AAC, M4* audio" = "AAC, M4* audio"; 27 | "Windows executable" = "Windows executable"; 28 | "QuickTime video" = "QuickTime video"; 29 | "OpenDocument Text" = "OpenDocument Text"; 30 | "Shell script" = "Shell script"; 31 | "CSV document" = "CSV document"; 32 | "gzip archive" = "gzip archive"; 33 | "Binary data" = "Binary data"; 34 | "OpenDocument Presentation" = "OpenDocument Presentation"; 35 | "vCard" = "vCard"; 36 | "Targa image" = "Targa image"; 37 | "Torrent" = "Torrent"; 38 | "XML document" = "XML document"; 39 | "MPEG audio" = "MPEG audio"; 40 | "OpenDocument Spreadsheet" = "OpenDocument Spreadsheet"; 41 | "Flash, MP4 video" = "Flash, MP4 video"; 42 | "Windows Icon" = "Windows Icon"; 43 | "Text document" = "Text document"; 44 | "GIF image" = "GIF image"; 45 | "MPEG video" = "MPEG video"; 46 | "Debian package" = "Debian package"; 47 | "Microsoft Word Document" = "Microsoft Word Document"; 48 | "WMV" = "WMV"; 49 | "Patch file" = "Patch file"; 50 | "RAR archive" = "RAR archive"; 51 | "Microsoft Excel Document" = "Microsoft Excel Document"; 52 | "OpenDocument Graphics" = "OpenDocument Graphics"; 53 | 54 | "7zip archive" = "7zip archive"; 55 | "Matroska audio" = "Matroska audio"; 56 | "Matroska video" = "Matroska video"; 57 | "Dynamic library" = "Dynamic library"; 58 | "iPhone application" = "iPhone application"; 59 | "iPhone/iPod software update" = "iPhone/iPod software update"; 60 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Safari Downloader/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "DOWNLOADS_TITLE" = "Downloads"; 2 | "DOWNLOAD_TARGET" = "Download Target"; 3 | "DOWNLOAD_IMAGE" = "Download Image"; 4 | "STATUS_RETRYING" = "Retrying..."; 5 | "STATUS_FAILED" = "Failed - Tap to Retry"; 6 | "STATUS_CANCELLED" = "Cancelled"; 7 | "STATUS_WAITING_AUTH" = "Awaiting Authentication..."; 8 | "STATUS_WAITING" = "Waiting..."; 9 | "STATUS_DOWNLOADING_SPEED" = "Downloading @ %@/s"; 10 | "PERCENTAGE" = "%u%%"; 11 | "CANCEL_ALL_PROMPT" = "Cancel All Downloads?"; 12 | "CLEAR_ALL_PROMPT" = "Clear All Downloads?"; 13 | "YES" = "Yes"; 14 | "NO" = "No"; 15 | "OK" = "OK"; 16 | "RETRY" = "Retry"; 17 | "DELETE" = "Delete"; 18 | "CANCEL" = "Cancel"; 19 | "CLEAR" = "Clear"; 20 | "DONE" = "Done"; 21 | "CANCEL_ALL_SHORT" = "Cancel All"; 22 | "CLEAR_ALL_SHORT" = "Clear All"; 23 | "NOTHING_TO_CANCEL" = "Nothing to Cancel"; 24 | "NOTHING_TO_CLEAR" = "Nothing to Clear"; 25 | "SIZE_BYTES" = "%.1lf B"; 26 | "SIZE_KILOBYTES" = "%.1lf KB"; 27 | "SIZE_MEGABYTES" = "%.1lf MB"; 28 | 29 | // Download Prompt 30 | "DOWNLOAD_PROMPT_TITLE" = "Download"; 31 | "SAVE_INTO" = "Destination"; 32 | "ACTION_DOWNLOAD" = "Download"; 33 | "ACTION_VIEW" = "View"; 34 | "ACTION_CANCEL" = "Cancel"; 35 | "ACTION_CHOOSE_DIRECTORY" = "Choose"; 36 | "ACTION_SAVE_AS" = "Save Here"; 37 | 38 | // Download Actions 39 | "OPEN_WITH_" = "Open in %@"; 40 | 41 | // Other 42 | "SAVE_AS_PROMPT_" = "Choose a destination for \"%@\"."; 43 | "SAVE_AS_PROMPT" = "Choose a destination for the file."; 44 | "NEW_DIRECTORY_TITLE" = "New Folder"; 45 | "NEW_DIRECTORY_NAME_PLACEHOLDER" = "Folder Name"; 46 | "ACTION_CREATE_DIRECTORY" = "Create"; 47 | "HOME_FOLDER" = "Home Folder"; 48 | -------------------------------------------------------------------------------- /layout/Library/MobileSubstrate/DynamicLibraries/Downloader.plist: -------------------------------------------------------------------------------- 1 | {Filter = {Bundles = ("com.apple.mobilesafari");};} 2 | -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/SafariDownload.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | SafariDownloaderSettings 9 | cell 10 | PSLinkCell 11 | isController 12 | 13 | icon 14 | icon.png 15 | label 16 | Safari Download Manager 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /preferences/Makefile: -------------------------------------------------------------------------------- 1 | include ../framework/makefiles/common.mk 2 | 3 | BUNDLE_NAME := SDSettings 4 | SDSettings_FILES = SDSettings.mm 5 | SDSettings_SUBPROJECTS = ../common:filetypes 6 | 7 | SDSettings_INSTALL_PATH = /Library/PreferenceBundles 8 | 9 | SDSettings_FRAMEWORKS = UIKit CoreGraphics 10 | SDSettings_PRIVATE_FRAMEWORKS = Preferences 11 | 12 | SDSettings_CFLAGS = -I$(THEOS_PROJECT_DIR) -I$(THEOS_PROJECT_DIR)/preferences -mthumb -DSDFILETYPE_MAP_CATEGORIES=1 -DSDFILETYPE_NO_CUSTOM=1 -DBUNDLE_PATH="@\"$(SDSettings_INSTALL_PATH)/$(SDSettings_BUNDLE_NAME).bundle\"" 13 | SDSettings_LDFLAGS = -mthumb 14 | 15 | SDSettings_BUNDLE_NAME = SafariDownloaderSettings 16 | 17 | include ../framework/makefiles/bundle.mk 18 | -------------------------------------------------------------------------------- /preferences/Resources/AutoRetry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | 11 | 12 | cell 13 | PSSwitchCell 14 | default 15 | 16 | defaults 17 | net.howett.safaridownloader 18 | key 19 | AutoRetryEnabled 20 | label 21 | AUTO_RETRY_ENABLED 22 | 23 | 24 | cell 25 | PSLinkListCell 26 | default 27 | 3 28 | defaults 29 | net.howett.safaridownloader 30 | detail 31 | PSListItemsController 32 | key 33 | AutoRetryCount 34 | label 35 | RETRY_ATTEMPTS 36 | validTitles 37 | 38 | RETRY_ATTEMPTS_1 39 | RETRY_ATTEMPTS_2 40 | RETRY_ATTEMPTS_3 41 | RETRY_ATTEMPTS_4 42 | RETRY_ATTEMPTS_5 43 | 44 | validValues 45 | 46 | 1 47 | 2 48 | 3 49 | 4 50 | 5 51 | 52 | 53 | 54 | cell 55 | PSGroupCell 56 | label 57 | RETRY_DELAY 58 | 59 | 60 | cell 61 | PSSliderCell 62 | default 63 | 1 64 | defaults 65 | net.howett.safaridownloader 66 | key 67 | AutoRetryInterval 68 | label 69 | Delay 70 | max 71 | 5 72 | min 73 | 0 74 | showValue 75 | 76 | 77 | 78 | title 79 | AUTO_RETRY 80 | 81 | 82 | -------------------------------------------------------------------------------- /preferences/Resources/FileClass.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | isStaticText 11 | 12 | footerText 13 | FILE_TYPE_DISABLE_EXPLANATION 14 | 15 | 16 | cell 17 | PSStaticTextCell 18 | label 19 | FILE_TYPE_DISABLE_EXPLANATION 20 | legacy 21 | 22 | 23 | cell 24 | PSGroupCell 25 | label 26 | BUILT_IN_TYPES 27 | 28 | 29 | cell 30 | PSGroupCell 31 | label 32 | CUSTOM 33 | 34 | 35 | cell 36 | PSLinkCell 37 | customControllerClass 38 | SDSettingsCustomFileTypeController 39 | detail 40 | SDFileTypeSetupController 41 | label 42 | ADD_FILE_TYPE 43 | prompt 44 | ADD_FILE_TYPE_PROMPT 45 | okTitleSAVE 46 | cancelTitleCANCEL 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /preferences/Resources/FileType.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | label 11 | DETAILS 12 | 13 | 14 | autoCaps 15 | word 16 | cell 17 | PSEditTextCell 18 | get 19 | getName: 20 | id 21 | name 22 | label 23 | NAME 24 | noAutoCorrect 25 | 26 | placeholder 27 | NAME_PLACEHOLDER 28 | set 29 | setName:forSpecifier: 30 | 31 | 32 | cell 33 | PSGroupCell 34 | label 35 | EXTENSIONS_GROUP 36 | 37 | 38 | cell 39 | PSEditTextCell 40 | id 41 | newExt 42 | itemType 43 | extension 44 | new 45 | 46 | noAutoCorrect 47 | 48 | placeholder 49 | EXTENSIONS_PLACEHOLDER 50 | set 51 | setNewItem:forSpecifier: 52 | 53 | 54 | cell 55 | PSGroupCell 56 | label 57 | MIMETYPES_GROUP 58 | 59 | 60 | cell 61 | PSEditTextCell 62 | id 63 | newMime 64 | itemType 65 | mimetype 66 | new 67 | 68 | noAutoCorrect 69 | 70 | placeholder 71 | MIMETYPES_PLACEHOLDER 72 | set 73 | setNewItem:forSpecifier: 74 | 75 | 76 | cell 77 | PSGroupCell 78 | 79 | 80 | action 81 | deleteButtonPressed: 82 | cell 83 | PSButtonCell 84 | id 85 | delete 86 | label 87 | DELETE_FILE_TYPE 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /preferences/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | SDSettings 9 | CFBundleIdentifier 10 | net.howett.safaridownload.settings 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | DTPlatformName 22 | iphoneos 23 | MinimumOSVersion 24 | 3.0 25 | NSPrincipalClass 26 | SDSettingsController 27 | 28 | 29 | -------------------------------------------------------------------------------- /preferences/Resources/SafariDownloader.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | 11 | 12 | cell 13 | PSSwitchCell 14 | default 15 | 16 | defaults 17 | net.howett.safaridownloader 18 | key 19 | Enabled 20 | label 21 | DOWNLOADER_ENABLED 22 | 23 | 24 | cell 25 | PSGroupCell 26 | 27 | 28 | cell 29 | PSLinkCell 30 | detail 31 | SDSettingsFileClassListController 32 | label 33 | FILE_TYPES 34 | 35 | 45 | 46 | cell 47 | PSLinkListCell 48 | default 49 | 5 50 | defaults 51 | net.howett.safaridownloader 52 | detail 53 | PSListItemsController 54 | key 55 | MaxConcurrentDownloads 56 | label 57 | MAX_CONCURRENT_DOWNLOADS 58 | validTitles 59 | 60 | 1 61 | 2 62 | 3 63 | 4 64 | 5 65 | 6 66 | 7 67 | 8 68 | 9 69 | 10 70 | 71 | validValues 72 | 73 | 1 74 | 2 75 | 3 76 | 4 77 | 5 78 | 6 79 | 7 80 | 8 81 | 9 82 | 10 83 | 84 | 85 | 86 | cell 87 | PSGroupCell 88 | isStaticText 89 | 90 | footerText 91 | © 2009-2012 Dustin Howett, Youssef Francis 92 | 93 | 94 | cell 95 | PSTitleValueCell 96 | label 97 | © 2009-2012 Dustin Howett, Youssef Francis 98 | legacy 99 | 100 | 101 | title 102 | Safari Downloader 103 | 104 | 105 | -------------------------------------------------------------------------------- /preferences/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "DOWNLOADER_ENABLED" = "Download Files"; 2 | "FILE_TYPES" = "File Types"; 3 | "AUTO_RETRY" = "Auto-Retry"; 4 | "AUTO_RETRY_SETTINGS" = "Auto-Retry Settings"; 5 | "MAX_CONCURRENT_DOWNLOADS" = "Max. Concurrent Downloads"; 6 | 7 | "AUTO_RETRY_ENABLED" = "Auto-Retry Failed"; 8 | "RETRY_ATTEMPTS" = "Retry Attempts"; 9 | 10 | "RETRY_ATTEMPTS_1" = "1 attempt"; 11 | "RETRY_ATTEMPTS_2" = "2 attempts"; 12 | "RETRY_ATTEMPTS_3" = "3 attempts"; 13 | "RETRY_ATTEMPTS_4" = "4 attempts"; 14 | "RETRY_ATTEMPTS_5" = "5 attempts"; 15 | 16 | "RETRY_DELAY" = "Auto-Retry Delay (Seconds)"; 17 | 18 | "FILE_TYPE_DISABLE_EXPLANATION" = "Disabling a file type will cause Safari to not prompt you with regards to downloading a file of that type, and instead behave normally."; 19 | "BUILT_IN_TYPES" = "Built-In Types"; 20 | "CUSTOM" = "Custom"; 21 | "ADD_FILE_TYPE" = "Add File Type..."; 22 | "NEW_FILE_TYPE" = "New File Type"; 23 | "ADD_FILE_TYPE_PROMPT" = "Enter new file type information"; 24 | "EDIT_FILE_TYPE_PROMPT" = "Enter the information for this file type"; 25 | "SAVE" = "Save"; 26 | "CANCEL" = "Cancel"; 27 | 28 | "DETAILS" = "Details"; 29 | "NAME" = "Name"; 30 | "NAME_PLACEHOLDER" = "Type Name"; 31 | "EXTENSIONS_GROUP" = "Extensions"; 32 | "EXTENSIONS_PLACEHOLDER" = "Extension"; 33 | "MIMETYPES_GROUP" = "MIME Types"; 34 | "MIMETYPES_PLACEHOLDER" = "MIME Type"; 35 | "DELETE_FILE_TYPE" = "Delete File Type"; 36 | -------------------------------------------------------------------------------- /preferences/SDSettings.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | @interface SDListController : PSListController 8 | - (id)specifiers; 9 | @end 10 | 11 | @interface SDSettingsController : SDListController 12 | - (id)initForContentSize:(CGSize)size; 13 | @end 14 | -------------------------------------------------------------------------------- /preferences/common: -------------------------------------------------------------------------------- 1 | ../common -------------------------------------------------------------------------------- /release.h: -------------------------------------------------------------------------------- 1 | #ifdef RELEASE 2 | #define NSLog(...) 3 | #endif 4 | -------------------------------------------------------------------------------- /sbsettings/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | PWD:=$(shell pwd) 3 | TOP_DIR:=$(PWD) 4 | FRAMEWORKDIR=$(TOP_DIR)/../framework 5 | 6 | TARGET=arm-apple-darwin9 7 | SDKPREFIX=/opt/iphone-sdk-3.0/prefix/bin 8 | PREFIX:=$(SDKPREFIX)/$(TARGET)- 9 | 10 | CC=$(PREFIX)gcc 11 | CXX=$(PREFIX)g++ 12 | STRIP=$(PREFIX)strip 13 | CODESIGN_ALLOCATE=$(PREFIX)codesign_allocate 14 | export CC CXX STRIP CODESIGN_ALLOCATE 15 | 16 | LDFLAGS:=-lobjc -bind_at_load -framework Foundation -framework CoreFoundation \ 17 | -multiply_defined suppress -dynamiclib -Wall \ 18 | -Werror -ObjC++ -fobjc-exceptions -fobjc-call-cxx-cdtors $(LDFLAGS) 19 | 20 | CFLAGS:=-Os -mthumb $(DEBUG_CFLAGS) -I$(FRAMEWORKDIR)/include 21 | export FRAMEWORKDIR 22 | export CFLAGS 23 | 24 | OFILES=main.o 25 | 26 | TARGET=Toggle.dylib 27 | 28 | all: $(TARGET) 29 | 30 | $(TARGET): $(OFILES) 31 | $(CXX) $(LDFLAGS) -o $@ $^ 32 | $(STRIP) -x $@ 33 | CODESIGN_ALLOCATE=$(CODESIGN_ALLOCATE) ldid -S $@ 34 | 35 | %.o: %.mm 36 | $(CXX) -c $(CFLAGS) $< -o $@ 37 | 38 | %.o: %.m 39 | $(CXX) -c $(CFLAGS) $< -o $@ 40 | 41 | clean: 42 | rm -f $(TARGET) *.o 43 | 44 | package-local: $(TARGET) 45 | cp $(TARGET) _/var/mobile/Library/SBSettings/Toggles/SDM 46 | chown 0.80 _ -R 47 | 48 | include $(FRAMEWORKDIR)/makefiles/DebMakefile 49 | -------------------------------------------------------------------------------- /sbsettings/layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: net.howett.safaridownloadersbs 2 | Name: SDM SBSettings 3 | Pre-Depends: firmware (>= 3.0) 4 | Depends: net.howett.safaridownloader (>= 1.3.0-1), sbsettings (>= 1.0.7-5) 5 | Version: 1.0.0 6 | Architecture: iphoneos-arm 7 | Description: An SBSettings toggle for Safari Download Manager 8 | Maintainer: Dustin Howett 9 | Author: Dustin Howett 10 | Section: SBSettings Addons 11 | Depiction: http://howett.net/cydia/depict/sdm/toggle.html 12 | Homepage: http://howett.net/cydia/depict/sdm/toggle.html 13 | Sponsor: thebigboss.org 14 | dev: dustinhowett 15 | tag: cydia::commercial 16 | -------------------------------------------------------------------------------- /sbsettings/main.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define PREFERENCES_FILE @"/var/mobile/Library/Preferences/net.howett.safaridownloader.plist" 4 | 5 | extern "C" BOOL isCapable() { 6 | return YES; 7 | } 8 | 9 | extern "C" BOOL isEnabled() { 10 | NSDictionary *userPrefs = [NSDictionary dictionaryWithContentsOfFile:PREFERENCES_FILE]; 11 | BOOL disabled = [[userPrefs objectForKey:@"Disabled"] boolValue]; 12 | return !disabled; 13 | } 14 | 15 | extern "C" float getDelayTime() { return 0.25f; } 16 | 17 | extern "C" void setState(BOOL enabled) { 18 | BOOL disabled = !enabled; 19 | NSMutableDictionary *userPrefs = [NSMutableDictionary dictionaryWithContentsOfFile:PREFERENCES_FILE]; 20 | [userPrefs setValue:[NSNumber numberWithBool:disabled] forKey:@"Disabled"]; 21 | [userPrefs writeToFile:PREFERENCES_FILE atomically:NO]; 22 | CFNotificationCenterRef r = CFNotificationCenterGetDarwinNotifyCenter(); 23 | CFNotificationCenterPostNotification(r, CFSTR("net.howett.safaridownloader/ReloadPrefs"), NULL, NULL, true); 24 | return; 25 | } 26 | --------------------------------------------------------------------------------