1/****************************************************************************** 2 * $Id: BlocklistDownloader.m 13326 2012-05-29 01:03:21Z livings124 $ 3 * 4 * Copyright (c) 2008-2012 Transmission authors and contributors 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 *****************************************************************************/ 24 25#import "BlocklistDownloader.h" 26#import "BlocklistDownloaderViewController.h" 27#import "BlocklistScheduler.h" 28#import "Controller.h" 29 30@interface BlocklistDownloader (Private) 31 32- (void) startDownload; 33- (void) decompressBlocklist; 34 35@end 36 37@implementation BlocklistDownloader 38 39BlocklistDownloader * fBLDownloader = nil; 40+ (BlocklistDownloader *) downloader 41{ 42 if (!fBLDownloader) 43 { 44 fBLDownloader = [[BlocklistDownloader alloc] init]; 45 [fBLDownloader startDownload]; 46 } 47 48 return fBLDownloader; 49} 50 51+ (BOOL) isRunning 52{ 53 return fBLDownloader != nil; 54} 55 56- (void) setViewController: (BlocklistDownloaderViewController *) viewController 57{ 58 fViewController = viewController; 59 if (fViewController) 60 { 61 switch (fState) 62 { 63 case BLOCKLIST_DL_START: 64 [fViewController setStatusStarting]; 65 break; 66 case BLOCKLIST_DL_DOWNLOADING: 67 [fViewController setStatusProgressForCurrentSize: fCurrentSize expectedSize: fExpectedSize]; 68 break; 69 case BLOCKLIST_DL_PROCESSING: 70 [fViewController setStatusProcessing]; 71 break; 72 } 73 } 74} 75 76- (void) dealloc 77{ 78 [fDownload release]; 79 [fDestination release]; 80 [super dealloc]; 81} 82 83- (void) cancelDownload 84{ 85 [fViewController setFinished]; 86 87 [fDownload cancel]; 88 89 [[BlocklistScheduler scheduler] updateSchedule]; 90 91 fBLDownloader = nil; 92 [self release]; 93} 94 95//using the actual filename is the best bet 96- (void) download: (NSURLDownload *) download decideDestinationWithSuggestedFilename: (NSString *) filename 97{ 98 [fDownload setDestination: [NSTemporaryDirectory() stringByAppendingPathComponent: filename] allowOverwrite: NO]; 99} 100 101- (void) download: (NSURLDownload *) download didCreateDestination: (NSString *) path 102{ 103 [fDestination release]; 104 fDestination = [path retain]; 105} 106 107- (void) download: (NSURLDownload *) download didReceiveResponse: (NSURLResponse *) response 108{ 109 fState = BLOCKLIST_DL_DOWNLOADING; 110 111 fCurrentSize = 0; 112 fExpectedSize = [response expectedContentLength]; 113 114 [fViewController setStatusProgressForCurrentSize: fCurrentSize expectedSize: fExpectedSize]; 115} 116 117- (void) download: (NSURLDownload *) download didReceiveDataOfLength: (NSUInteger) length 118{ 119 fCurrentSize += length; 120 [fViewController setStatusProgressForCurrentSize: fCurrentSize expectedSize: fExpectedSize]; 121} 122 123- (void) download: (NSURLDownload *) download didFailWithError: (NSError *) error 124{ 125 [fViewController setFailed: [error localizedDescription]]; 126 127 [[NSUserDefaults standardUserDefaults] setObject: [NSDate date] forKey: @"BlocklistNewLastUpdate"]; 128 [[BlocklistScheduler scheduler] updateSchedule]; 129 130 fBLDownloader = nil; 131 [self release]; 132} 133 134- (void) downloadDidFinish: (NSURLDownload *) download 135{ 136 fState = BLOCKLIST_DL_PROCESSING; 137 138 [fViewController setStatusProcessing]; 139 140 NSAssert(fDestination != nil, @"the blocklist file destination has not been specified"); 141 142 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 143 [self decompressBlocklist]; 144 145 dispatch_async(dispatch_get_main_queue(), ^{ 146 const int count = tr_blocklistSetContent([(Controller *)[NSApp delegate] sessionHandle], [fDestination UTF8String]); 147 148 //delete downloaded file 149 [[NSFileManager defaultManager] removeItemAtPath: fDestination error: NULL]; 150 151 if (count > 0) 152 [fViewController setFinished]; 153 else 154 [fViewController setFailed: NSLocalizedString(@"The specified blocklist file did not contain any valid rules.", 155 "blocklist fail message")]; 156 157 //update last updated date for schedule 158 NSDate * date = [NSDate date]; 159 [[NSUserDefaults standardUserDefaults] setObject: date forKey: @"BlocklistNewLastUpdate"]; 160 [[NSUserDefaults standardUserDefaults] setObject: date forKey: @"BlocklistNewLastUpdateSuccess"]; 161 [[BlocklistScheduler scheduler] updateSchedule]; 162 163 [[NSNotificationCenter defaultCenter] postNotificationName: @"BlocklistUpdated" object: nil]; 164 165 fBLDownloader = nil; 166 [self release]; 167 }); 168 }); 169} 170 171- (BOOL) download: (NSURLDownload *) download shouldDecodeSourceDataOfMIMEType: (NSString *) encodingType 172{ 173 return YES; 174} 175 176@end 177 178@implementation BlocklistDownloader (Private) 179 180- (void) startDownload 181{ 182 fState = BLOCKLIST_DL_START; 183 184 [[BlocklistScheduler scheduler] cancelSchedule]; 185 186 NSString * urlString = [[NSUserDefaults standardUserDefaults] stringForKey: @"BlocklistURL"]; 187 if (!urlString) 188 urlString = @""; 189 else if (![urlString isEqualToString: @""] && [urlString rangeOfString: @"://"].location == NSNotFound) 190 urlString = [@"http://" stringByAppendingString: urlString]; 191 192 NSURLRequest * request = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString]]; 193 194 fDownload = [[NSURLDownload alloc] initWithRequest: request delegate: self]; 195} 196 197//.gz, .tar.gz, .tgz, and .bgz will be decompressed by NSURLDownload for us. However, we have to do .zip files manually. 198- (void) decompressBlocklist 199{ 200 if ([[[fDestination pathExtension] lowercaseString] isEqualToString: @"zip"]) { 201 BOOL success = NO; 202 203 NSString * workingDirectory = [fDestination stringByDeletingLastPathComponent]; 204 205 //First, perform the actual unzipping 206 NSTask * unzip = [[NSTask alloc] init]; 207 [unzip setLaunchPath: @"/usr/bin/unzip"]; 208 [unzip setCurrentDirectoryPath: workingDirectory]; 209 [unzip setArguments: [NSArray arrayWithObjects: 210 @"-o", /* overwrite */ 211 @"-q", /* quiet! */ 212 fDestination, /* source zip file */ 213 @"-d", workingDirectory, /*destination*/ 214 nil]]; 215 216 @try 217 { 218 [unzip launch]; 219 [unzip waitUntilExit]; 220 221 if ([unzip terminationStatus] == 0) 222 success = YES; 223 } 224 @catch(id exc) 225 { 226 success = NO; 227 } 228 [unzip release]; 229 230 if (success) { 231 //Now find out what file we actually extracted; don't just assume it matches the zipfile's name 232 NSTask *zipinfo; 233 234 zipinfo = [[NSTask alloc] init]; 235 [zipinfo setLaunchPath: @"/usr/bin/zipinfo"]; 236 [zipinfo setArguments: [NSArray arrayWithObjects: 237 @"-1", /* just the filename */ 238 fDestination, /* source zip file */ 239 nil]]; 240 [zipinfo setStandardOutput: [NSPipe pipe]]; 241 242 @try 243 { 244 NSFileHandle * zipinfoOutput = [[zipinfo standardOutput] fileHandleForReading]; 245 246 [zipinfo launch]; 247 [zipinfo waitUntilExit]; 248 249 NSString * actualFilename = [[[NSString alloc] initWithData: [zipinfoOutput readDataToEndOfFile] 250 encoding: NSUTF8StringEncoding] autorelease]; 251 actualFilename = [actualFilename stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 252 NSString * newBlocklistPath = [workingDirectory stringByAppendingPathComponent: actualFilename]; 253 254 //Finally, delete the ZIP file; we're done with it, and we'll return the unzipped blocklist 255 [[NSFileManager defaultManager] removeItemAtPath: fDestination error: NULL]; 256 257 [fDestination release]; 258 fDestination = [newBlocklistPath retain]; 259 } 260 @catch(id exc) {} 261 [zipinfo release]; 262 } 263 } 264} 265 266@end 267