merged [21403]: Fixed an exception thrown when clearing all complete file transfers...
[adiumx.git] / Source / XtrasInstaller.m
blobd3303f5b89b35711ddf223f32bbbb67d0a5a3eba
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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 @end
29 /*!
30  * @class XtrasInstaller
31  * @brief Class which displays a progress window and downloads an AdiumXtra, decompresses it, and installs it.
32  */
33 @implementation XtrasInstaller
35 //XtrasInstaller does not autorelease because it will release itself when closed
36 + (XtrasInstaller *)installer
38         return [[XtrasInstaller alloc] init];
41 - (id)init
43         if ((self = [super init])) {
44                 download = nil;
45                 window = nil;
46         }
48         return self;
51 - (void)dealloc
53         [download release];
55         [super dealloc];
58 - (IBAction)cancel:(id)sender;
60         if (download) [download cancel];
61         [self closeInstaller];
64 - (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
66         [self cancel:nil];
69 - (void)closeInstaller
71         if (window) [window close];
72         [self autorelease];     
75 - (void)installXtraAtURL:(NSURL *)url
77         if ([[url host] isEqualToString:@"www.adiumxtras.com"] || ALLOW_UNTRUSTED_XTRAS) {
78                 NSURL   *urlToDownload;
80                 [NSBundle loadNibNamed:@"XtraProgressWindow" owner:self];
81                 [progressBar setUsesThreadedAnimation:YES];
82                 
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];
97         } else {
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),
101                                                 nil, nil);
102                 [self closeInstaller];
103         }
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)]];
128         }
129         else
130                 [progressBar setIndeterminate:YES];
133 - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
134     return NO;
137 - (void)download:(NSURLDownload *)inDownload didFailWithError:(NSError *)error {
138         NSString        *errorMsg;
140         errorMsg = [NSString stringWithFormat:AILocalizedString(@"An error occurred while downloading this Xtra: %@.",nil),[error localizedDescription]];
141         
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;
150         
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]];
158                 
159                 @try
160                 {
161                         [uncompress launch];
162                         [uncompress waitUntilExit];
163                 }
164                 @catch(id exc)
165                 {
166                         decompressionSuccess = NO;      
167                 }
168                         
169                 [uncompress release];
170                 
171                 if (decompressionSuccess) {
172                         if ([pathExtension isEqualToString:@"tgz"]) {
173                                 dest = [[dest stringByDeletingPathExtension] stringByAppendingPathExtension:@"tar"];
174                         } else {
175                                 //hasSuffix .tar.gz
176                                 dest = [dest substringToIndex:[dest length] - 3];//remove the .gz, leaving us with .tar
177                         }
178                         
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]];
183                         
184                         @try
185                         {
186                                 [untar launch];
187                                 [untar waitUntilExit];
188                         }
189                         @catch(id exc)
190                         {
191                                 decompressionSuccess = NO;
192                         }
193                         [untar release];
194                 }
195                 
196         } else if ([pathExtension isEqualToString:@"zip"]) {
197                 NSTask  *unzip;
198                 
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 */
204                         @"-q", /* quiet! */
205                         dest, /* source zip file */
206                         @"-d", [dest stringByDeletingLastPathComponent], /*destination folder*/
207                         nil]];
209                 [unzip setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
211                 @try
212                 {
213                         [unzip launch];
214                         [unzip waitUntilExit];
215                 }
216                 @catch(id exc)
217                 {
218                         decompressionSuccess = NO;                      
219                 }
220                 [unzip release];
222         } else {
223                 decompressionSuccess = NO;
224         }
225         
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];
231         
232         dest = [dest stringByDeletingLastPathComponent];
233         
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) {
239                 NSString                *xtraPath;
240                 NSString                *nextFile;
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.
246                          */
247                         if ((![[nextFile lastPathComponent] hasPrefix:@"."]) &&
248                                 (![[nextFile pathComponents] containsObject:@"__MACOSX"])) {
249                                 NSString                *fileExtension = [nextFile pathExtension];
250                                 NSEnumerator    *supportedDocumentExtensionsEnumerator;
251                                 NSString                *extension;
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);
259                                 }
261                                 if (isSupported) {
262                                         BOOL    success;
264                                         xtraPath = [dest stringByAppendingPathComponent:nextFile];
266                                         //Open the file directly
267                                         AILog(@"Installing %@",xtraPath);
268                                         success = [[NSApp delegate] application:NSApp
269                                                                                            openTempFile:xtraPath];
271                                         if (!success) {
272                                                 NSLog(@"Installation Error: %@",xtraPath);
273                                         }
274                                 }
275                         }
276                 }
277                 
278         } else {
279                 NSLog(@"Installation Error: %@ (%@)",dest, (decompressionSuccess ? @"Decompressed succesfully" : @"Failed to decompress"));
280         }
281         
282         //delete our temporary directory, and any files remaining in it
283         [fileManager removeFileAtPath:dest handler:nil];
284         
285         [self closeInstaller];
288 @end