2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "XtrasInstaller.h"
18 #import <AIUtilities/AIApplicationAdditions.h>
19 #import <AIUtilities/AIBundleAdditions.h>
20 #import <AIUtilities/AIStringAdditions.h>
22 //Should only be YES for testing
23 #define ALLOW_UNTRUSTED_XTRAS NO
25 @interface XtrasInstaller (PRIVATE)
26 - (void)closeInstaller;
30 * @class XtrasInstaller
31 * @brief Class which displays a progress window and downloads an AdiumXtra, decompresses it, and installs it.
33 @implementation XtrasInstaller
35 //XtrasInstaller does not autorelease because it will release itself when closed
36 + (XtrasInstaller *)installer
38 return [[XtrasInstaller alloc] init];
43 if ((self = [super init])) {
58 - (IBAction)cancel:(id)sender;
60 if (download) [download cancel];
61 [self closeInstaller];
64 - (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
69 - (void)closeInstaller
71 if (window) [window close];
75 - (void)installXtraAtURL:(NSURL *)url
77 if ([[url host] isEqualToString:@"www.adiumxtras.com"] || ALLOW_UNTRUSTED_XTRAS) {
80 [NSBundle loadNibNamed:@"XtraProgressWindow" owner:self];
81 [progressBar setUsesThreadedAnimation:YES];
83 [progressBar setDoubleValue:0];
84 [percentText setStringValue:@"0%"];
85 [cancelButton setLocalizedString:AILocalizedString(@"Cancel",nil)];
86 [window setTitle:AILocalizedString(@"Xtra Download",nil)];
87 [window makeKeyAndOrderFront:self];
89 urlToDownload = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@/%@?%@", @"http", [url host], [url path], [url query]]];
90 // dest = [NSTemporaryDirectory() stringByAppendingPathComponent:[[urlToDownload path] lastPathComponent]];
92 download = [[NSURLDownload alloc] initWithRequest:[NSURLRequest requestWithURL:urlToDownload] delegate:self];
93 // [download setDestination:dest allowOverwrite:YES];
95 [urlToDownload release];
98 NSRunAlertPanel(AILocalizedString(@"Nontrusted Xtra", nil),
99 AILocalizedString(@"This Xtra is not hosted by adiumxtras.com. Automatic installation is not allowed.", nil),
100 AILocalizedString(@"Cancel", nil),
102 [self closeInstaller];
106 - (void)download:(NSURLDownload *)connection didReceiveResponse:(NSURLResponse *)response
108 amountDownloaded = 0;
109 downloadSize = [response expectedContentLength];
110 [progressBar setMaxValue:(long long)downloadSize];
111 [progressBar setDoubleValue:0.0];
114 - (void)download:(NSURLDownload *)connection decideDestinationWithSuggestedFilename:(NSString *)filename
116 NSString * downloadDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString uuid]];
117 [[NSFileManager defaultManager] createDirectoryAtPath:downloadDir attributes:nil];
118 dest = [downloadDir stringByAppendingPathComponent:filename];
119 [download setDestination:dest allowOverwrite:YES];
122 - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length
124 amountDownloaded += (long long)length;
125 if (downloadSize != NSURLResponseUnknownLength) {
126 [progressBar setDoubleValue:(double)amountDownloaded];
127 [percentText setStringValue:[NSString stringWithFormat:@"%f%",(double)((amountDownloaded / (double)downloadSize) * 100)]];
130 [progressBar setIndeterminate:YES];
133 - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
137 - (void)download:(NSURLDownload *)inDownload didFailWithError:(NSError *)error {
140 errorMsg = [NSString stringWithFormat:AILocalizedString(@"An error occurred while downloading this Xtra: %@.",nil),[error localizedDescription]];
142 NSBeginAlertSheet(AILocalizedString(@"Xtra Downloading Error",nil), AILocalizedString(@"Cancel",nil), nil, nil, window, self,
143 NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), nil, errorMsg);
146 - (void)downloadDidFinish:(NSURLDownload *)download {
147 NSString *lastPathComponent = [[dest lowercaseString] lastPathComponent];
148 NSString *pathExtension = [lastPathComponent pathExtension];
149 BOOL decompressionSuccess = YES;
151 if ([pathExtension isEqualToString:@"tgz"] || [lastPathComponent hasSuffix:@".tar.gz"]) {
152 NSTask *uncompress, *untar;
154 uncompress = [[NSTask alloc] init];
155 [uncompress setLaunchPath:@"/usr/bin/gunzip"];
156 [uncompress setArguments:[NSArray arrayWithObjects:@"-df" , [dest lastPathComponent] , nil]];
157 [uncompress setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
162 [uncompress waitUntilExit];
166 decompressionSuccess = NO;
169 [uncompress release];
171 if (decompressionSuccess) {
172 if ([pathExtension isEqualToString:@"tgz"]) {
173 dest = [[dest stringByDeletingPathExtension] stringByAppendingPathExtension:@"tar"];
176 dest = [dest substringToIndex:[dest length] - 3];//remove the .gz, leaving us with .tar
179 untar = [[NSTask alloc] init];
180 [untar setLaunchPath:@"/usr/bin/tar"];
181 [untar setArguments:[NSArray arrayWithObjects:@"-xvf", [dest lastPathComponent], nil]];
182 [untar setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
187 [untar waitUntilExit];
191 decompressionSuccess = NO;
196 } else if ([pathExtension isEqualToString:@"zip"]) {
199 //First, perform the actual unzipping
200 unzip = [[NSTask alloc] init];
201 [unzip setLaunchPath:@"/usr/bin/unzip"];
202 [unzip setArguments:[NSArray arrayWithObjects:
203 @"-o", /* overwrite */
205 dest, /* source zip file */
206 @"-d", [dest stringByDeletingLastPathComponent], /*destination folder*/
209 [unzip setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
214 [unzip waitUntilExit];
218 decompressionSuccess = NO;
223 decompressionSuccess = NO;
226 NSFileManager *fileManager = [NSFileManager defaultManager];
227 NSEnumerator *fileEnumerator;
229 //Delete the compressed xtra, now that we've decompressed it
230 [fileManager removeFileAtPath:dest handler:nil];
232 dest = [dest stringByDeletingLastPathComponent];
234 //the remaining files in the directory should be the contents of the xtra
235 fileEnumerator = [fileManager enumeratorAtPath:dest];
236 AILog(@"Downloaded to %@. fileEnumerator: %@",dest,fileEnumerator);
238 if (decompressionSuccess && fileEnumerator) {
241 NSSet *supportedDocumentExtensions = [[NSBundle mainBundle] supportedDocumentExtensions];
243 while((nextFile = [fileEnumerator nextObject])) {
244 /* Ignore hidden files and the __MACOSX folder which some compression engines stick into the archive but
245 * /usr/bin/unzip doesn't handle properly.
247 if ((![[nextFile lastPathComponent] hasPrefix:@"."]) &&
248 (![[nextFile pathComponents] containsObject:@"__MACOSX"])) {
249 NSString *fileExtension = [nextFile pathExtension];
250 NSEnumerator *supportedDocumentExtensionsEnumerator;
252 BOOL isSupported = NO;
254 //We want to do a case-insensitive path extension comparison
255 supportedDocumentExtensionsEnumerator = [supportedDocumentExtensions objectEnumerator];
256 while (!isSupported &&
257 (extension = [supportedDocumentExtensionsEnumerator nextObject])) {
258 isSupported = ([fileExtension caseInsensitiveCompare:extension] == NSOrderedSame);
264 xtraPath = [dest stringByAppendingPathComponent:nextFile];
266 //Open the file directly
267 AILog(@"Installing %@",xtraPath);
268 success = [[NSApp delegate] application:NSApp
269 openTempFile:xtraPath];
272 NSLog(@"Installation Error: %@",xtraPath);
279 NSLog(@"Installation Error: %@ (%@)",dest, (decompressionSuccess ? @"Decompressed succesfully" : @"Failed to decompress"));
282 //delete our temporary directory, and any files remaining in it
283 [fileManager removeFileAtPath:dest handler:nil];
285 [self closeInstaller];