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/AIExceptionHandlingUtilities.h>
21 #import <AIUtilities/AIStringAdditions.h>
23 //Should only be YES for testing
24 #define ALLOW_UNTRUSTED_XTRAS NO
26 @interface XtrasInstaller (PRIVATE)
27 - (void)closeInstaller;
31 * @class XtrasInstaller
32 * @brief Class which displays a progress window and downloads an AdiumXtra, decompresses it, and installs it.
34 @implementation XtrasInstaller
36 //XtrasInstaller does not autorelease because it will release itself when closed
37 + (XtrasInstaller *)installer
39 return [[XtrasInstaller alloc] init];
44 if ((self = [super init])) {
59 - (IBAction)cancel:(id)sender;
61 if (download) [download cancel];
62 [self closeInstaller];
65 - (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
70 - (void)closeInstaller
72 if (window) [window close];
76 - (void)installXtraAtURL:(NSURL *)url
78 if ([[url host] isEqualToString:@"www.adiumxtras.com"] || ALLOW_UNTRUSTED_XTRAS) {
81 [NSBundle loadNibNamed:@"XtraProgressWindow" owner:self];
82 [progressBar setUsesThreadedAnimation:YES];
84 [progressBar setDoubleValue:0];
85 [percentText setStringValue:@"0%"];
86 [cancelButton setLocalizedString:AILocalizedString(@"Cancel",nil)];
87 [window setTitle:AILocalizedString(@"Xtra Download",nil)];
88 [window makeKeyAndOrderFront:self];
90 urlToDownload = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@/%@?%@", @"http", [url host], [url path], [url query]]];
91 // dest = [NSTemporaryDirectory() stringByAppendingPathComponent:[[urlToDownload path] lastPathComponent]];
93 download = [[NSURLDownload alloc] initWithRequest:[NSURLRequest requestWithURL:urlToDownload] delegate:self];
94 // [download setDestination:dest allowOverwrite:YES];
96 [urlToDownload release];
99 NSRunAlertPanel(AILocalizedString(@"Nontrusted Xtra", nil),
100 AILocalizedString(@"This Xtra is not hosted by adiumxtras.com. Automatic installation is not allowed.", nil),
101 AILocalizedString(@"Cancel", nil),
103 [self closeInstaller];
107 - (void)download:(NSURLDownload *)connection didReceiveResponse:(NSURLResponse *)response
109 amountDownloaded = 0;
110 downloadSize = [response expectedContentLength];
111 [progressBar setMaxValue:(long long)downloadSize];
112 [progressBar setDoubleValue:0.0];
115 - (void)download:(NSURLDownload *)connection decideDestinationWithSuggestedFilename:(NSString *)filename
117 NSString * downloadDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString uuid]];
118 [[NSFileManager defaultManager] createDirectoryAtPath:downloadDir attributes:nil];
119 dest = [downloadDir stringByAppendingPathComponent:filename];
120 [download setDestination:dest allowOverwrite:YES];
123 - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length
125 amountDownloaded += (long long)length;
126 if (downloadSize != NSURLResponseUnknownLength) {
127 [progressBar setDoubleValue:(double)amountDownloaded];
128 [percentText setStringValue:[NSString stringWithFormat:@"%f%",(double)((amountDownloaded / (double)downloadSize) * 100)]];
131 [progressBar setIndeterminate:YES];
134 - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
138 - (void)download:(NSURLDownload *)inDownload didFailWithError:(NSError *)error {
141 errorMsg = [NSString stringWithFormat:AILocalizedString(@"An error occurred while downloading this Xtra: %@.",nil),[error localizedDescription]];
143 NSBeginAlertSheet(AILocalizedString(@"Xtra Downloading Error",nil), AILocalizedString(@"Cancel",nil), nil, nil, window, self,
144 NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), nil, errorMsg);
147 - (void)downloadDidFinish:(NSURLDownload *)download {
148 NSString *lastPathComponent = [[dest lowercaseString] lastPathComponent];
149 NSString *pathExtension = [lastPathComponent pathExtension];
150 BOOL decompressionSuccess = YES;
152 if ([pathExtension isEqualToString:@"tgz"] || [lastPathComponent hasSuffix:@".tar.gz"]) {
153 NSTask *uncompress, *untar;
155 uncompress = [[NSTask alloc] init];
156 [uncompress setLaunchPath:@"/usr/bin/gunzip"];
157 [uncompress setArguments:[NSArray arrayWithObjects:@"-df" , [dest lastPathComponent] , nil]];
158 [uncompress setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
163 [uncompress waitUntilExit];
167 decompressionSuccess = NO;
171 [uncompress release];
173 if (decompressionSuccess) {
174 if ([pathExtension isEqualToString:@"tgz"]) {
175 dest = [[dest stringByDeletingPathExtension] stringByAppendingPathExtension:@"tar"];
178 dest = [dest substringToIndex:[dest length] - 3];//remove the .gz, leaving us with .tar
181 untar = [[NSTask alloc] init];
182 [untar setLaunchPath:@"/usr/bin/tar"];
183 [untar setArguments:[NSArray arrayWithObjects:@"-xvf", [dest lastPathComponent], nil]];
184 [untar setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
189 [untar waitUntilExit];
193 decompressionSuccess = NO;
199 } else if ([pathExtension isEqualToString:@"zip"]) {
202 //First, perform the actual unzipping
203 unzip = [[NSTask alloc] init];
204 [unzip setLaunchPath:@"/usr/bin/unzip"];
205 [unzip setArguments:[NSArray arrayWithObjects:
206 @"-o", /* overwrite */
208 dest, /* source zip file */
209 @"-d", [dest stringByDeletingLastPathComponent], /*destination folder*/
212 [unzip setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
217 [unzip waitUntilExit];
221 decompressionSuccess = NO;
227 decompressionSuccess = NO;
230 NSFileManager *fileManager = [NSFileManager defaultManager];
231 NSEnumerator *fileEnumerator;
233 //Delete the compressed xtra, now that we've decompressed it
234 [fileManager removeFileAtPath:dest handler:nil];
236 dest = [dest stringByDeletingLastPathComponent];
238 //the remaining files in the directory should be the contents of the xtra
239 fileEnumerator = [fileManager enumeratorAtPath:dest];
240 AILog(@"Downloaded to %@. fileEnumerator: %@",dest,fileEnumerator);
242 if (decompressionSuccess && fileEnumerator) {
245 NSSet *supportedDocumentExtensions = [[NSBundle mainBundle] supportedDocumentExtensions];
247 while((nextFile = [fileEnumerator nextObject])) {
248 /* Ignore hidden files and the __MACOSX folder which some compression engines stick into the archive but
249 * /usr/bin/unzip doesn't handle properly.
251 if ((![[nextFile lastPathComponent] hasPrefix:@"."]) &&
252 (![[nextFile pathComponents] containsObject:@"__MACOSX"])) {
253 NSString *fileExtension = [nextFile pathExtension];
254 NSEnumerator *supportedDocumentExtensionsEnumerator;
256 BOOL isSupported = NO;
258 //We want to do a case-insensitive path extension comparison
259 supportedDocumentExtensionsEnumerator = [supportedDocumentExtensions objectEnumerator];
260 while (!isSupported &&
261 (extension = [supportedDocumentExtensionsEnumerator nextObject])) {
262 isSupported = ([fileExtension caseInsensitiveCompare:extension] == NSOrderedSame);
268 xtraPath = [dest stringByAppendingPathComponent:nextFile];
270 //Open the file directly
271 AILog(@"Installing %@",xtraPath);
272 success = [[NSApp delegate] application:NSApp
273 openTempFile:xtraPath];
276 NSLog(@"Installation Error: %@",xtraPath);
283 NSLog(@"Installation Error: %@ (%@)",dest, (decompressionSuccess ? @"Decompressed succesfully" : @"Failed to decompress"));
286 //delete our temporary directory, and any files remaining in it
287 [fileManager removeFileAtPath:dest handler:nil];
289 [self closeInstaller];