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;
27 - (void)updateInfoText;
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];
88 [progressBar setDoubleValue:0];
89 [cancelButton setLocalizedString:AILocalizedString(@"Cancel",nil)];
90 [window setTitle:AILocalizedString(@"Xtra Download",nil)];
92 [self updateInfoText];
94 [window makeKeyAndOrderFront:self];
96 urlToDownload = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@/%@?%@", @"http", [url host], [url path], [url query]]];
97 // dest = [NSTemporaryDirectory() stringByAppendingPathComponent:[[urlToDownload path] lastPathComponent]];
99 download = [[NSURLDownload alloc] initWithRequest:[NSURLRequest requestWithURL:urlToDownload] delegate:self];
100 // [download setDestination:dest allowOverwrite:YES];
102 [urlToDownload release];
105 NSRunAlertPanel(AILocalizedString(@"Nontrusted Xtra", nil),
106 AILocalizedString(@"This Xtra is not hosted by adiumxtras.com. Automatic installation is not allowed.", nil),
107 AILocalizedString(@"Cancel", nil),
109 [self closeInstaller];
113 - (void)updateInfoText
115 int percentComplete = (downloadSize > 0 ? (int)(((double)amountDownloaded / (double)downloadSize) * 100.0) : 0);
116 NSString *installText = [NSString stringWithFormat:AILocalizedString(@"Downloading %@", @"Install an Xtra; %@ is the name of the Xtra."), (xtraName ? xtraName : @"")];
118 [infoText setStringValue:[NSString stringWithFormat:@"%@ (%d%%)", installText, percentComplete]];
121 - (void)download:(NSURLDownload *)connection didReceiveResponse:(NSHTTPURLResponse *)response
123 xtraName = [[response allHeaderFields] objectForKey:@"X-Xtraname"];
124 amountDownloaded = 0;
125 downloadSize = [response expectedContentLength];
126 [progressBar setMaxValue:(long long)downloadSize];
127 [progressBar setDoubleValue:0.0];
128 [self updateInfoText];
131 - (void)download:(NSURLDownload *)connection decideDestinationWithSuggestedFilename:(NSString *)filename
133 NSString * downloadDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString uuid]];
134 [[NSFileManager defaultManager] createDirectoryAtPath:downloadDir attributes:nil];
135 dest = [downloadDir stringByAppendingPathComponent:filename];
136 [download setDestination:dest allowOverwrite:YES];
139 - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length
141 amountDownloaded += (long long)length;
142 if (downloadSize != NSURLResponseUnknownLength) {
143 [progressBar setDoubleValue:(double)amountDownloaded];
144 [self updateInfoText];
147 [progressBar setIndeterminate:YES];
150 - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
154 - (void)download:(NSURLDownload *)inDownload didFailWithError:(NSError *)error {
157 errorMsg = [NSString stringWithFormat:AILocalizedString(@"An error occurred while downloading this Xtra: %@.",nil),[error localizedDescription]];
159 NSBeginAlertSheet(AILocalizedString(@"Xtra Downloading Error",nil), AILocalizedString(@"Cancel",nil), nil, nil, window, self,
160 NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), nil, errorMsg);
163 - (void)downloadDidFinish:(NSURLDownload *)download {
164 NSString *lastPathComponent = [[dest lowercaseString] lastPathComponent];
165 NSString *pathExtension = [lastPathComponent pathExtension];
166 BOOL decompressionSuccess = YES;
168 if ([pathExtension isEqualToString:@"tgz"] || [lastPathComponent hasSuffix:@".tar.gz"]) {
169 NSTask *uncompress, *untar;
171 uncompress = [[NSTask alloc] init];
172 [uncompress setLaunchPath:@"/usr/bin/gunzip"];
173 [uncompress setArguments:[NSArray arrayWithObjects:@"-df" , [dest lastPathComponent] , nil]];
174 [uncompress setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
179 [uncompress waitUntilExit];
183 decompressionSuccess = NO;
186 [uncompress release];
188 if (decompressionSuccess) {
189 if ([pathExtension isEqualToString:@"tgz"]) {
190 dest = [[dest stringByDeletingPathExtension] stringByAppendingPathExtension:@"tar"];
193 dest = [dest substringToIndex:[dest length] - 3];//remove the .gz, leaving us with .tar
196 untar = [[NSTask alloc] init];
197 [untar setLaunchPath:@"/usr/bin/tar"];
198 [untar setArguments:[NSArray arrayWithObjects:@"-xvf", [dest lastPathComponent], nil]];
199 [untar setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
204 [untar waitUntilExit];
208 decompressionSuccess = NO;
213 } else if ([pathExtension isEqualToString:@"zip"]) {
216 //First, perform the actual unzipping
217 unzip = [[NSTask alloc] init];
218 [unzip setLaunchPath:@"/usr/bin/unzip"];
219 [unzip setArguments:[NSArray arrayWithObjects:
220 @"-o", /* overwrite */
222 dest, /* source zip file */
223 @"-d", [dest stringByDeletingLastPathComponent], /*destination folder*/
226 [unzip setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
231 [unzip waitUntilExit];
235 decompressionSuccess = NO;
240 decompressionSuccess = NO;
243 NSFileManager *fileManager = [NSFileManager defaultManager];
244 NSEnumerator *fileEnumerator;
246 //Delete the compressed xtra, now that we've decompressed it
247 [fileManager removeFileAtPath:dest handler:nil];
249 dest = [dest stringByDeletingLastPathComponent];
251 //the remaining files in the directory should be the contents of the xtra
252 fileEnumerator = [fileManager enumeratorAtPath:dest];
253 AILog(@"Downloaded to %@. fileEnumerator: %@",dest,fileEnumerator);
255 if (decompressionSuccess && fileEnumerator) {
258 NSSet *supportedDocumentExtensions = [[NSBundle mainBundle] supportedDocumentExtensions];
260 while((nextFile = [fileEnumerator nextObject])) {
261 /* Ignore hidden files and the __MACOSX folder which some compression engines stick into the archive but
262 * /usr/bin/unzip doesn't handle properly.
264 if ((![[nextFile lastPathComponent] hasPrefix:@"."]) &&
265 (![[nextFile pathComponents] containsObject:@"__MACOSX"])) {
266 NSString *fileExtension = [nextFile pathExtension];
267 NSEnumerator *supportedDocumentExtensionsEnumerator;
269 BOOL isSupported = NO;
271 //We want to do a case-insensitive path extension comparison
272 supportedDocumentExtensionsEnumerator = [supportedDocumentExtensions objectEnumerator];
273 while (!isSupported &&
274 (extension = [supportedDocumentExtensionsEnumerator nextObject])) {
275 isSupported = ([fileExtension caseInsensitiveCompare:extension] == NSOrderedSame);
281 xtraPath = [dest stringByAppendingPathComponent:nextFile];
283 //Open the file directly
284 AILog(@"Installing %@",xtraPath);
285 success = [[NSApp delegate] application:NSApp
286 openTempFile:xtraPath];
289 NSLog(@"Installation Error: %@",xtraPath);
296 NSLog(@"Installation Error: %@ (%@)",dest, (decompressionSuccess ? @"Decompressed succesfully" : @"Failed to decompress"));
299 //delete our temporary directory, and any files remaining in it
300 [fileManager removeFileAtPath:dest handler:nil];
302 [self closeInstaller];