~ubuntu-branches/ubuntu/quantal/sunpinyin/quantal

« back to all changes in this revision

Viewing changes to wrapper/macos/packaging/SunPinyin/DownloadDataFiles/DownloadDataFilesPane.m

  • Committer: Package Import Robot
  • Author(s): YunQiang Su
  • Date: 2012-04-11 03:06:40 UTC
  • mfrom: (1.1.4) (1.2.8 sid)
  • Revision ID: package-import@ubuntu.com-20120411030640-8mxepz5e6wffy87c
Tags: 2.0.3+git20120404-1
* Medium urgency for fixing RC bug.
* New upstream commit: fix FTBFS with gcc-4.7 (Closes: #667385).
* Add Multi-Arch: same to libsunpinyin3, -dev and -dbg.
* Add YunQiang Su to uploaders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//
2
 
//  DownloadDataFilesPane.m
3
 
//  DownloadDataFiles
4
 
//
5
 
//  Created by Jjgod Jiang on 4/21/09.
6
 
 
7
 
/*
8
 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
9
 
 * 
10
 
 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
11
 
 * 
12
 
 * The contents of this file are subject to the terms of either the GNU Lesser
13
 
 * General Public License Version 2.1 only ("LGPL") or the Common Development and
14
 
 * Distribution License ("CDDL")(collectively, the "License"). You may not use this
15
 
 * file except in compliance with the License. You can obtain a copy of the CDDL at
16
 
 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
17
 
 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the 
18
 
 * specific language governing permissions and limitations under the License. When
19
 
 * distributing the software, include this License Header Notice in each file and
20
 
 * include the full text of the License in the License file as well as the
21
 
 * following notice:
22
 
 * 
23
 
 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
24
 
 * (CDDL)
25
 
 * For Covered Software in this distribution, this License shall be governed by the
26
 
 * laws of the State of California (excluding conflict-of-law provisions).
27
 
 * Any litigation relating to this License shall be subject to the jurisdiction of
28
 
 * the Federal Courts of the Northern District of California and the state courts
29
 
 * of the State of California, with venue lying in Santa Clara County, California.
30
 
 * 
31
 
 * Contributor(s):
32
 
 * 
33
 
 * If you wish your version of this file to be governed by only the CDDL or only
34
 
 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
35
 
 * include this software in this distribution under the [CDDL or LGPL Version 2.1]
36
 
 * license." If you don't indicate a single choice of license, a recipient has the
37
 
 * option to distribute your version of this file under either the CDDL or the LGPL
38
 
 * Version 2.1, or to extend the choice of license to its licensees as provided
39
 
 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
40
 
 * Version 2 license, then the option applies only if the new code is made subject
41
 
 * to such option by the copyright holder. 
42
 
 */
43
 
 
44
 
#import "DownloadDataFilesPane.h"
45
 
#import <CommonCrypto/CommonDigest.h>
46
 
#import <SecurityFoundation/SFAuthorization.h>
47
 
 
48
 
#define JJLocalizedString(key, comment) NSLocalizedStringFromTableInBundle(key, @"Localizable", [NSBundle bundleForClass: [self class]], comment)
49
 
 
50
 
#if __BIG_ENDIAN__
51
 
#define kDataFileExt    @".be"
52
 
#else
53
 
#define kDataFileExt    @".le"
54
 
#endif
55
 
 
56
 
NSDictionary *findInSpec(NSArray *specArray, NSString *file)
57
 
{
58
 
    NSString *fileNameByArch = [file stringByAppendingString: kDataFileExt];
59
 
 
60
 
    for (NSDictionary *dict in specArray)
61
 
        if ([[dict objectForKey: @"Name"] isEqual: fileNameByArch])
62
 
            return dict;
63
 
 
64
 
    return nil;
65
 
}
66
 
 
67
 
@interface DownloadDataFilesPane (Private)
68
 
 
69
 
- (BOOL) doAuthorizedCopyFromPath: (NSString *) src toPath: (NSString *) dest withAuthorization: (SFAuthorization *) authorization;
70
 
- (SFAuthorization *) prepareAuthorization;
71
 
 
72
 
@end
73
 
 
74
 
@implementation DownloadDataFilesPane
75
 
 
76
 
- (void) checkFile: (NSString *) file withSpec: (NSArray *) specArray
77
 
{
78
 
    NSString *targetDirectory = [self objectForKeyInBundle: @"TargetInstallDirectory"];
79
 
    NSString *path = [targetDirectory stringByAppendingPathComponent: file];
80
 
    NSDictionary *fileDict = findInSpec(specArray, file);
81
 
    
82
 
    if (! fileDict)
83
 
    {
84
 
        NSLog(@"Cannot find %@ in %@", file, specArray);
85
 
        return;
86
 
    }
87
 
 
88
 
    if ([[NSFileManager defaultManager] fileExistsAtPath: path])
89
 
    {
90
 
        NSString *expectedHash = [fileDict objectForKey: @"SHA256"];
91
 
        unsigned char hashedChars[CC_SHA256_DIGEST_LENGTH];
92
 
        NSData *data = [NSData dataWithContentsOfFile: path];
93
 
 
94
 
        CC_SHA256([data bytes], [data length], hashedChars);
95
 
    
96
 
        if (expectedHash)
97
 
        {
98
 
            int i;
99
 
            NSMutableString *fileHash = [NSMutableString stringWithCapacity: CC_SHA256_DIGEST_LENGTH * 2];
100
 
 
101
 
            for (i = 0; i < sizeof(hashedChars); i++)
102
 
                [fileHash appendFormat: @"%02x", hashedChars[i]];
103
 
 
104
 
            if (! [fileHash isEqualToString: expectedHash])
105
 
                NSLog(@"file hash mismatch: %@ (now) vs. %@ (expected)", fileHash, expectedHash);
106
 
            else
107
 
                // No need to update
108
 
                return;
109
 
        }
110
 
    }
111
 
 
112
 
    [filesToDownload addObject: fileDict];
113
 
    totalBytesToDownload += [(NSNumber *)[fileDict objectForKey: @"Size"] unsignedIntValue];
114
 
}
115
 
 
116
 
- (NSString *) title
117
 
{
118
 
        return [[NSBundle bundleForClass: [self class]] localizedStringForKey: @"PaneTitle"
119
 
                                                                    value: nil
120
 
                                                                    table: nil];
121
 
}
122
 
 
123
 
- (id) objectForKeyInBundle: (NSString *) key
124
 
{
125
 
        return [[[NSBundle bundleForClass: [self class]] infoDictionary] objectForKey: key];
126
 
}
127
 
 
128
 
- (void) finishDownload
129
 
{
130
 
    [mainTextField setStringValue: JJLocalizedString(@"All files are successfully downloaded.",
131
 
                                                     "All files downloaded.")];
132
 
    [auxTextField setHidden: YES];
133
 
    [getButton setHidden: YES];
134
 
    [progress setHidden: YES];
135
 
    
136
 
    [self setNextEnabled: YES];
137
 
    [self setPreviousEnabled: YES];
138
 
    
139
 
    isDownloading = NO;
140
 
    
141
 
    if (filesDownloaded)
142
 
    {
143
 
        // authorize
144
 
        SFAuthorization *authorization = [self prepareAuthorization];
145
 
        
146
 
        if (authorization)
147
 
        {
148
 
            for (NSString *file in filesDownloaded)
149
 
            {
150
 
                NSString *from = [@"/tmp" stringByAppendingPathComponent: file];
151
 
                NSString *to = [[self objectForKeyInBundle: @"TargetInstallDirectory"] stringByAppendingPathComponent: file];
152
 
                [self doAuthorizedCopyFromPath: from
153
 
                                        toPath: to
154
 
                             withAuthorization: authorization];
155
 
            }
156
 
 
157
 
            // deauthorize
158
 
            [authorization invalidateCredentials];
159
 
        }
160
 
    }
161
 
}
162
 
 
163
 
- (void) downloadNextFile
164
 
{
165
 
    NSDictionary *dict;
166
 
    isDownloading = YES;
167
 
    
168
 
    // If all files are downloaded
169
 
    if (! [filesToDownload count])
170
 
    {
171
 
        [self finishDownload];
172
 
        return;
173
 
    } else
174
 
        dict = [filesToDownload objectAtIndex: 0];
175
 
    
176
 
    NSURL *URL = [NSURL URLWithString: [dict objectForKey: @"URL"]];
177
 
    
178
 
    NSLog(@"Start downloading: %@", URL);
179
 
 
180
 
    // create the request
181
 
    NSURLRequest *theRequest = [NSURLRequest requestWithURL: URL
182
 
                                                cachePolicy: NSURLRequestUseProtocolCachePolicy
183
 
                                            timeoutInterval: 30.0];
184
 
    // create the connection with the request
185
 
    // and start loading the data
186
 
    NSURLDownload  *theDownload = [[NSURLDownload alloc] initWithRequest: theRequest
187
 
                                                                delegate: self];
188
 
    if (theDownload)
189
 
    {
190
 
        currentDownload = theDownload;
191
 
        NSString *destination = [@"/tmp" stringByAppendingPathComponent:
192
 
                                    [[dict objectForKey: @"Name"] stringByDeletingPathExtension]];
193
 
        NSLog(@"Set destination to %@", destination);
194
 
        // set the destination file now
195
 
        [theDownload setDestination: destination
196
 
                     allowOverwrite: YES];
197
 
    } else {
198
 
        // inform the user that the download could not be made
199
 
    }
200
 
}
201
 
 
202
 
- (NSString *) _humanReadableSizeFromDouble: (double) value
203
 
{
204
 
        if (value < 1024)
205
 
                return [NSString stringWithFormat:@"%.0lf %@", value,
206
 
                JJLocalizedString(@"B", @"the unit for bytes")];
207
 
        
208
 
        if (value < 1024 * 1024)
209
 
                return [NSString stringWithFormat:@"%.0lf %@", value / 1024.0,
210
 
                JJLocalizedString(@"KB", @"the unit for kilobytes")];
211
 
        
212
 
        if (value < 1024 * 1024 * 1024)
213
 
                return [NSString stringWithFormat:@"%.1lf %@", value / 1024.0 / 1024.0,
214
 
                JJLocalizedString(@"MB", @"the unit for megabytes")];
215
 
        
216
 
        return [NSString stringWithFormat:@"%.2lf %@", value / 1024.0 / 1024.0 / 1024.0,
217
 
            JJLocalizedString(@"GB", @"the unit for gigabytes")];       
218
 
}
219
 
 
220
 
- (IBAction) stopDownload: (id) sender
221
 
{
222
 
    [currentDownload cancel];
223
 
    [currentDownload release];
224
 
    currentDownload = nil;
225
 
 
226
 
    [getButton setTitle: JJLocalizedString(@"Start...", "Start downloading")];
227
 
    [getButton setAction: @selector(startDownload:)];
228
 
    [progress setDoubleValue: 0];
229
 
    [progress setHidden: YES];
230
 
    
231
 
    [self setNextEnabled: YES];
232
 
    [self setPreviousEnabled: YES];
233
 
    [auxTextField setStringValue: JJLocalizedString(@"Download stopped.", "Download stopped")];
234
 
}
235
 
 
236
 
- (IBAction) startDownload: (id) sender
237
 
{
238
 
    [progress setHidden: NO];
239
 
    [progress setDoubleValue: 0];
240
 
    
241
 
    [getButton setTitle: JJLocalizedString(@"Cancel", "Stop downloading")];
242
 
    [getButton setAction: @selector(stopDownload:)];
243
 
    [auxTextField setStringValue: [NSString stringWithFormat: JJLocalizedString(@"%@ of %@", nil), 
244
 
                                   [self _humanReadableSizeFromDouble: 0],
245
 
                                   [self _humanReadableSizeFromDouble: [progress maxValue]]]];
246
 
    
247
 
    [self setNextEnabled: NO];
248
 
    [self setPreviousEnabled: NO];
249
 
 
250
 
    [self downloadNextFile];
251
 
}
252
 
 
253
 
- (void) download: (NSURLDownload *) download didFailWithError: (NSError *) error
254
 
{
255
 
    [filesToDownload removeObjectAtIndex: 0];
256
 
    // release the connection
257
 
    [download release];
258
 
 
259
 
    [self downloadNextFile];
260
 
    // inform the user
261
 
    NSLog(@"Download failed! Error - %@ %@",
262
 
          [error localizedDescription],
263
 
          [[error userInfo] objectForKey: NSURLErrorFailingURLStringErrorKey]);
264
 
}
265
 
 
266
 
- (void) download: (NSURLDownload *) download didReceiveDataOfLength: (NSUInteger) length
267
 
{
268
 
    double value = [progress doubleValue] + length;
269
 
    if (value > [progress maxValue])
270
 
        value = [progress maxValue];
271
 
 
272
 
    [progress setDoubleValue: value];
273
 
    [auxTextField setStringValue: [NSString stringWithFormat: JJLocalizedString(@"%@ of %@", nil), 
274
 
                                   [self _humanReadableSizeFromDouble: value],
275
 
                                   [self _humanReadableSizeFromDouble: [progress maxValue]]]];
276
 
}
277
 
 
278
 
- (void) downloadDidFinish: (NSURLDownload *) download
279
 
{
280
 
    // release the connection
281
 
    [download release];
282
 
 
283
 
    NSString *filename = [[[[[download request] URL] path] lastPathComponent] stringByDeletingPathExtension];
284
 
    // do something with the data
285
 
    NSLog(@"Finish downloading: %@", filename);
286
 
 
287
 
    if ([filesToDownload count])
288
 
        [filesToDownload removeObjectAtIndex: 0];
289
 
 
290
 
    [filesDownloaded addObject: filename];
291
 
    [self downloadNextFile];
292
 
}
293
 
 
294
 
- (void) startFetchingPropertyListFromURL: (NSURL *) URL
295
 
{
296
 
    [mainTextField setStringValue: JJLocalizedString(@"Checking updates for data files...", "Checking property list on network")];
297
 
 
298
 
    NSURLRequest *request = [NSURLRequest requestWithURL: URL];
299
 
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest: request
300
 
                                                                  delegate: self];
301
 
    if (connection)
302
 
    {
303
 
        receivedData = [[NSMutableData data] retain];
304
 
    } else
305
 
    {
306
 
        // Failed to download plist
307
 
        NSLog(@"Failed to open %@", [URL path]);
308
 
        [self gotoNextPane];
309
 
    }
310
 
}
311
 
 
312
 
- (void) connection: (NSURLConnection *) connection didReceiveResponse: (NSURLResponse *) response
313
 
{
314
 
    [receivedData setLength: 0];
315
 
}
316
 
 
317
 
- (void) connection: (NSURLConnection *) connection didReceiveData: (NSData *) data
318
 
{
319
 
    [receivedData appendData: data];
320
 
}
321
 
 
322
 
- (void) connection: (NSURLConnection *) connection didFailWithError: (NSError *) error
323
 
{
324
 
    // release the connection, and the data object
325
 
    [connection release];
326
 
    // receivedData is declared as a method instance elsewhere
327
 
    [receivedData release];
328
 
    
329
 
    // inform the user
330
 
    NSLog(@"Connection failed! Error - %@ %@",
331
 
          [error localizedDescription],
332
 
          [[error userInfo] objectForKey: NSURLErrorFailingURLStringErrorKey]);
333
 
 
334
 
    [self gotoNextPane];
335
 
}
336
 
 
337
 
- (void) setSummaryString: (NSString *) summary detailString: (NSString *) detail
338
 
{
339
 
    NSDictionary *summaryAttributes, *detailAttributes;
340
 
    summaryAttributes = [NSDictionary dictionaryWithObject: [NSFont fontWithName: @"Lucida Grande" size: 13.0]
341
 
                                                    forKey: NSFontAttributeName];
342
 
    detailAttributes = [NSDictionary dictionaryWithObject: [NSFont fontWithName: @"Monaco" size: 11.0]
343
 
                                                   forKey: NSFontAttributeName];
344
 
 
345
 
    NSMutableAttributedString *stringToSet = [[NSMutableAttributedString alloc] initWithString: summary
346
 
                                                                                    attributes: summaryAttributes];
347
 
    NSAttributedString *detailString = [[NSAttributedString alloc] initWithString: detail
348
 
                                                                       attributes: detailAttributes];
349
 
    [stringToSet appendAttributedString: [[[NSAttributedString alloc] initWithString: @"\n\n"] autorelease]];
350
 
    [stringToSet appendAttributedString: detailString];
351
 
    [detailString release];
352
 
    [mainTextField setAttributedStringValue: stringToSet];
353
 
    [stringToSet release];
354
 
}
355
 
 
356
 
- (void) connectionDidFinishLoading: (NSURLConnection *) connection
357
 
{
358
 
    [connection release];
359
 
 
360
 
    NSString *errorDesc = nil;
361
 
    NSArray *specArray = (NSArray *)[NSPropertyListSerialization propertyListFromData: receivedData
362
 
                                                                     mutabilityOption: NSPropertyListImmutable
363
 
                                                                               format: NULL
364
 
                                                                     errorDescription: &errorDesc];
365
 
    [receivedData release];
366
 
 
367
 
    if (! specArray)
368
 
    {
369
 
        NSLog(@"Failed to read property list: %@", errorDesc);
370
 
        
371
 
        NSString *errorTitle = [NSString stringWithFormat: JJLocalizedString(@"Failed to fetch a valid data file list from %@:", nil),
372
 
                                [self objectForKeyInBundle: @"FilesSpecPlistURL"]];
373
 
        
374
 
        [self setSummaryString: errorTitle
375
 
                  detailString: errorDesc];
376
 
 
377
 
        [errorDesc release];
378
 
        return;
379
 
    }
380
 
 
381
 
    NSArray *files = [NSArray arrayWithObjects: @"lm_sc.t3g", @"pydict_sc.bin", nil];
382
 
 
383
 
    if (! filesToDownload)
384
 
        filesToDownload = [[NSMutableArray alloc] initWithCapacity: 2];
385
 
    
386
 
    if (! filesDownloaded)
387
 
        filesDownloaded = [[NSMutableArray alloc] initWithCapacity: 2];
388
 
 
389
 
    totalBytesToDownload = 0;
390
 
    isDownloading = NO;
391
 
 
392
 
    for (NSString *file in files)
393
 
        [self checkFile: file withSpec: specArray];
394
 
    
395
 
    if ([filesToDownload count])
396
 
    {
397
 
        NSString *text = [NSString stringWithFormat: JJLocalizedString(@"The following files need to be downloaded and save to\n%@:",
398
 
                                                                       "Need To Download"),
399
 
                          [self objectForKeyInBundle: @"TargetInstallDirectory"]];
400
 
        NSMutableString *urls = [NSMutableString stringWithCapacity: 50];
401
 
        for (NSDictionary *dict in filesToDownload)
402
 
            [urls appendFormat: @"%@\n", [dict objectForKey: @"URL"]];
403
 
 
404
 
        [self setSummaryString: text
405
 
                  detailString: urls];
406
 
 
407
 
        [auxTextField setStringValue: JJLocalizedString(@"Click the Start button to download them automatically.",
408
 
                                                        "Click Download Button")];
409
 
        [getButton setTitle: JJLocalizedString(@"Start...", "Start downloading")];
410
 
        [getButton setHidden: NO];
411
 
        [progress setStyle: NSProgressIndicatorBarStyle];
412
 
        [progress setMaxValue: totalBytesToDownload];
413
 
        [progress setIndeterminate: NO];
414
 
    }
415
 
    else
416
 
        [mainTextField setStringValue: JJLocalizedString(@"You already have the latest data files, just Continue.", "Bypass")];
417
 
}
418
 
 
419
 
/* pane's entry point: code called when user enters this pane */
420
 
- (void) didEnterPane: (InstallerSectionDirection) dir
421
 
{
422
 
    NSURL *plistURL = [NSURL URLWithString: [self objectForKeyInBundle: @"FilesSpecPlistURL"]];
423
 
    [self startFetchingPropertyListFromURL: plistURL];
424
 
}
425
 
 
426
 
/* called when user clicks "Continue" -- return value indicates if application should exit pane */
427
 
- (BOOL) shouldExitPane: (InstallerSectionDirection) dir
428
 
{
429
 
    if (isDownloading)
430
 
        return NO;
431
 
    
432
 
    [filesToDownload release];
433
 
    filesToDownload = nil;
434
 
 
435
 
        return YES;
436
 
}
437
 
 
438
 
- (SFAuthorization *) prepareAuthorization
439
 
{
440
 
    // authorize
441
 
    AuthorizationFlags authFlags =  kAuthorizationFlagPreAuthorize |
442
 
    kAuthorizationFlagExtendRights |
443
 
    kAuthorizationFlagInteractionAllowed;
444
 
    AuthorizationItem authItem = { kAuthorizationRightExecute, 0, nil, 0 };
445
 
    AuthorizationRights authRights = { 1, &authItem };
446
 
    SFAuthorization *authorization = [SFAuthorization authorizationWithFlags: authFlags
447
 
                                                                      rights: &authRights
448
 
                                                                 environment: kAuthorizationEmptyEnvironment];
449
 
    return authorization;
450
 
}
451
 
 
452
 
// doAuthorizedCopyFromPath does an authorized copy, getting admin rights
453
 
//
454
 
// NOTE: when running the task with admin privileges, this waits on any child
455
 
// process, since AEWP doesn't tell us the child's pid.  This could be fooled
456
 
// by any other child process that quits in the window between launch and
457
 
// completion of our actual tool.
458
 
- (BOOL) doAuthorizedCopyFromPath: (NSString *) src 
459
 
                           toPath: (NSString *) dest
460
 
                withAuthorization: (SFAuthorization *) authorization
461
 
{    
462
 
    // execute the copy
463
 
    const char taskPath[] = "/usr/bin/ditto";
464
 
    const char* arguments[] = {
465
 
        "-rsrcFork",  // 0: copy resource forks; --rsrc requires 10.3
466
 
        NULL,  // 1: src path
467
 
        NULL,  // 2: dest path
468
 
        NULL
469
 
    };
470
 
    arguments[1] = [src fileSystemRepresentation];
471
 
    arguments[2] = [dest fileSystemRepresentation];
472
 
    
473
 
    FILE **kNoPipe = nil;
474
 
    OSStatus status = AuthorizationExecuteWithPrivileges([authorization authorizationRef],
475
 
                                                         taskPath,
476
 
                                                         kAuthorizationFlagDefaults,
477
 
                                                         (char *const *)arguments,
478
 
                                                         kNoPipe);
479
 
    if (status == errAuthorizationSuccess) {
480
 
        int wait_status;
481
 
        int pid = wait(&wait_status);
482
 
        if (pid == -1 || !WIFEXITED(wait_status))   {
483
 
            status = -1;
484
 
        } else {
485
 
            status = WEXITSTATUS(wait_status);
486
 
        }
487
 
    }
488
 
 
489
 
    return (status == 0);
490
 
}
491
 
 
492
 
@end