~tcurdt/sparkle/devel

« back to all changes in this revision

Viewing changes to SUUnarchiver.m

  • Committer: Andy Matuschak
  • Date: 2008-06-19 05:53:16 UTC
  • Revision ID: andy@andymatuschak.org-20080619055316-zktlbz5mxa9ezvs5
Fixes 236695

Refactored Sparkle's unarchiving system into SUUnarchiver, a factory for SUPipedUnarchiver and SUDiskImageUnarchiver. I removed that nasty cleanUp call by now copying out the contents of the DMG into the /tmp directory and unmounting. Nice!

This changed a fair amount so please test with your build and let me know if it explodes things. Works in my tests, though.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 
10
10
#import "Sparkle.h"
11
11
#import "SUUnarchiver.h"
 
12
#import "SUUnarchiver_Private.h"
12
13
 
13
14
@implementation SUUnarchiver
14
15
 
15
 
// This method abstracts the types that use a command line tool piping data from stdin.
16
 
- (BOOL)_extractArchivePipingDataToCommand:(NSString *)command
17
 
{
18
 
        // Get the file size.
19
 
        NSNumber *fs = [[[NSFileManager defaultManager] fileAttributesAtPath:archivePath traverseLink:NO] objectForKey:NSFileSize];
20
 
        if (fs == nil) { return NO; }
21
 
                
22
 
        // Thank you, Allan Odgaard!
23
 
        // (who wrote the following extraction alg.)
24
 
        
25
 
        long current = 0;
26
 
        FILE *fp, *cmdFP;
27
 
        if ((fp = fopen([archivePath fileSystemRepresentation], "r")))
28
 
        {
29
 
                setenv("DESTINATION", [[archivePath stringByDeletingLastPathComponent] UTF8String], 1);
30
 
                if ((cmdFP = popen([command fileSystemRepresentation], "w")))
31
 
                {
32
 
                        char buf[32*1024];
33
 
                        long len;
34
 
                        while((len = fread(buf, 1, 32 * 1024, fp)))
35
 
                        {                               
36
 
                                current += len;
37
 
                                
38
 
                                NSEvent *event;
39
 
                                while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]))
40
 
                                        [NSApp sendEvent:event];
41
 
                                
42
 
                                fwrite(buf, 1, len, cmdFP);
43
 
                                
44
 
                                [self performSelectorOnMainThread:@selector(notifyDelegateOfExtractedLength:) withObject:[NSNumber numberWithLong:len] waitUntilDone:NO];
45
 
                        }
46
 
                        pclose(cmdFP);
47
 
                }
48
 
                fclose(fp);
49
 
        }       
50
 
 
51
 
        return YES;
52
 
}
53
 
 
54
 
- (BOOL)_extractTAR
55
 
{
56
 
        return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""];
57
 
}
58
 
 
59
 
- (BOOL)_extractTGZ
60
 
{
61
 
        return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""];
62
 
}
63
 
 
64
 
- (BOOL)_extractTBZ
65
 
{
66
 
        return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""];
67
 
}
68
 
 
69
 
- (BOOL)_extractZIP
70
 
{
71
 
        return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""];
72
 
}
73
 
 
74
 
- (BOOL)_extractDMG
75
 
{               
76
 
        // get a unique mount point path
77
 
        NSString *mountPoint = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"mp"];
78
 
        int cnt=1;
79
 
        while ([[NSFileManager defaultManager] fileExistsAtPath:mountPoint] && cnt <= 999)
80
 
                mountPoint = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"mp%d", cnt++]];
81
 
        
82
 
        if (![[NSFileManager defaultManager] fileExistsAtPath:mountPoint])
83
 
        {               
84
 
                // create mount point folder
85
 
                [[NSFileManager defaultManager] createDirectoryAtPath:mountPoint attributes:nil];
86
 
                
87
 
                if ([[NSFileManager defaultManager] fileExistsAtPath:mountPoint])
88
 
                {
89
 
                        NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, @"-noverify", @"-nobrowse", @"-noautoopen", nil];
90
 
                        // set up a pipe and push "yes" (y works too), this will accept any license agreement crap
91
 
                        // not every .dmg needs this, but this will make sure it works with everyone
92
 
                        NSData* yesData = [[[NSData alloc] initWithBytes:"yes\n" length:4] autorelease];
93
 
                        
94
 
                        [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData];
95
 
                }
96
 
        }
97
 
        
98
 
        return YES;
99
 
}
100
 
 
101
 
- (void)_unarchive
102
 
{
103
 
        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
104
 
 
105
 
        // This dictionary associates names of methods responsible for extraction with file extensions.
106
 
        // The methods take the path of the archive to extract. They return a BOOL indicating whether
107
 
        // we should continue with the update; returns NO if an error occurred.
108
 
        NSDictionary *commandDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
109
 
                                                                                                                                   @"_extractTBZ", @".tbz",
110
 
                                                                                                                                   @"_extractTBZ", @".tar.bz2",
111
 
                                                                                                                                   @"_extractTGZ", @".tgz",
112
 
                                                                                                                                   @"_extractTGZ", @".tar.gz",
113
 
                                                                                                                                   @"_extractTAR", @".tar", 
114
 
                                                                                                                                   @"_extractZIP", @".zip", 
115
 
                                                                                                                                   @"_extractDMG", @".dmg",
116
 
                                                                                                                                   nil];
117
 
        SEL command = NULL;
118
 
        NSString *theLastPathComponent = [[archivePath lastPathComponent] lowercaseString];
119
 
        NSEnumerator *theEnumerator = [[commandDictionary allKeys] objectEnumerator];
120
 
        NSString *theExtension = NULL;
121
 
        while ((theExtension = [theEnumerator nextObject]) != NULL)
122
 
        {
123
 
                if ([[theLastPathComponent substringFromIndex:[theLastPathComponent length] - [theExtension length]] isEqualToString:theExtension])
124
 
                {
125
 
                        command = NSSelectorFromString([commandDictionary objectForKey:theExtension]);
126
 
                        break;
127
 
                }
128
 
        }
129
 
        
130
 
        BOOL result;
131
 
        if (command)
132
 
        {
133
 
                NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:command]];
134
 
                [invocation setSelector:command];
135
 
                [invocation invokeWithTarget:self];
136
 
                [invocation getReturnValue:&result];
137
 
        }
138
 
        else
139
 
                result = NO;
140
 
        
141
 
        if (result)
142
 
                [self performSelectorOnMainThread:@selector(notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
143
 
        else
144
 
                [self performSelectorOnMainThread:@selector(notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
145
 
 
146
 
        [pool release];
147
 
}
148
 
 
149
 
- (void)unarchivePath:(NSString *)path
150
 
{
151
 
        archivePath = [path copy];
152
 
        [NSThread detachNewThreadSelector:@selector(_unarchive) toTarget:self withObject:nil];
 
16
extern NSMutableArray *__unarchiverImplementations;
 
17
 
 
18
+ (SUUnarchiver *)unarchiverForURL:(NSURL *)URL
 
19
{
 
20
        NSEnumerator *implementationEnumerator = [[self _unarchiverImplementations] objectEnumerator];
 
21
        id current;
 
22
        while ((current = [implementationEnumerator nextObject]))
 
23
        {
 
24
                if ([current _canUnarchiveURL:URL])
 
25
                        return [[[current alloc] _initWithURL:URL] autorelease];
 
26
        }
 
27
        return nil;
153
28
}
154
29
 
155
30
- (void)setDelegate:del
157
32
        delegate = del;
158
33
}
159
34
 
160
 
- (void)cleanUp
161
 
{
162
 
        if ([[archivePath pathExtension] isEqualToString:@".dmg"])
163
 
        {
164
 
                [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", [archivePath stringByDeletingLastPathComponent], @"-force", nil]];        
165
 
        }
166
 
        [[NSFileManager defaultManager] removeFileAtPath:archivePath handler:nil];
167
 
}
168
 
 
169
 
- (void)dealloc
170
 
{
171
 
        [archivePath release];
172
 
        [super dealloc];
173
 
}
174
 
 
175
 
- (void)notifyDelegateOfExtractedLength:(long)length
176
 
{
177
 
        if ([delegate respondsToSelector:@selector(unarchiver:extractedLength:)])
178
 
                [delegate unarchiver:self extractedLength:length];
179
 
}
180
 
 
181
 
- (void)notifyDelegateOfSuccess
182
 
{
183
 
        if ([delegate respondsToSelector:@selector(unarchiverDidFinish:)])
184
 
                [delegate performSelector:@selector(unarchiverDidFinish:) withObject:self];
185
 
}
186
 
 
187
 
- (void)notifyDelegateOfFailure
188
 
{
189
 
        if ([delegate respondsToSelector:@selector(unarchiverDidFail:)])
190
 
                [delegate performSelector:@selector(unarchiverDidFail:) withObject:self];
 
35
- (void)start
 
36
{
 
37
        // No-op
191
38
}
192
39
 
193
40
@end