Skip to content

Commit

Permalink
Use GPGStream for file operations
Browse files Browse the repository at this point in the history
  • Loading branch information
idodeclare committed May 22, 2012
1 parent acbc74c commit 5035fb1
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 40 deletions.
6 changes: 6 additions & 0 deletions GPGServices.xcodeproj/project.pbxproj
Expand Up @@ -23,6 +23,7 @@
30B2057714D56F2A00AE9583 /* asc.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8ED7990D135CAB60004C89D5 /* asc.icns */; };
30B2057814D56F2A00AE9583 /* lock.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8ED7990C135CAB60004C89D5 /* lock.icns */; };
30B2057914D56F2A00AE9583 /* sig.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8ED7990B135CAB60004C89D5 /* sig.icns */; };
451D886C156ABFAD00A0B890 /* GPGTempFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 451D886B156ABFAD00A0B890 /* GPGTempFile.m */; };
45C2B9E31565AEC300B3571A /* WorkerProgressViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C2B9E21565AEC300B3571A /* WorkerProgressViewItem.m */; };
45C2B9EA1565DD6600B3571A /* NSAlert+ThreadSafety.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C2B9E91565DD6600B3571A /* NSAlert+ThreadSafety.m */; };
45CC89011569365300928F5B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 45CC88FF1569365300928F5B /* Localizable.strings */; };
Expand Down Expand Up @@ -148,6 +149,8 @@
30B2055D14D56EC700AE9583 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/VerificationResultsWindow.xib; sourceTree = "<group>"; };
30B2055E14D56EC700AE9583 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/VerificationResultsWindow.xib.strings; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* GPGServices_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPGServices_Prefix.pch; path = Source/GPGServices_Prefix.pch; sourceTree = "<group>"; };
451D886A156ABFAD00A0B890 /* GPGTempFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPGTempFile.h; path = Source/GPGTempFile.h; sourceTree = "<group>"; };
451D886B156ABFAD00A0B890 /* GPGTempFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPGTempFile.m; path = Source/GPGTempFile.m; sourceTree = "<group>"; };
45C2B9E11565AEC300B3571A /* WorkerProgressViewItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WorkerProgressViewItem.h; path = Source/WorkerProgressViewItem.h; sourceTree = "<group>"; };
45C2B9E21565AEC300B3571A /* WorkerProgressViewItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WorkerProgressViewItem.m; path = Source/WorkerProgressViewItem.m; sourceTree = "<group>"; };
45C2B9E81565DD6600B3571A /* NSAlert+ThreadSafety.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSAlert+ThreadSafety.h"; path = "Source/NSAlert+ThreadSafety.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -219,6 +222,8 @@
4B1908C00A4CBF0B00052798 /* GPGServices.m */,
8EDC40F6133F6C5500D0A101 /* ZipOperation.h */,
8EDC40F7133F6C5500D0A101 /* ZipOperation.m */,
451D886A156ABFAD00A0B890 /* GPGTempFile.h */,
451D886B156ABFAD00A0B890 /* GPGTempFile.m */,
8EE899891348B92B004A3266 /* NSPredicate+negate.h */,
8E1BC426134D013600C1BFD6 /* GPGKey+utils.h */,
8E1BC427134D013600C1BFD6 /* GPGKey+utils.m */,
Expand Down Expand Up @@ -564,6 +569,7 @@
45C2B9E31565AEC300B3571A /* WorkerProgressViewItem.m in Sources */,
45C2B9EA1565DD6600B3571A /* NSAlert+ThreadSafety.m in Sources */,
45FC40DB1566D2D80020A12B /* ServiceWrappedArgs.m in Sources */,
451D886C156ABFAD00A0B890 /* GPGTempFile.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
6 changes: 6 additions & 0 deletions Resources/en.lproj/Localizable.strings
Expand Up @@ -13,6 +13,12 @@
/* arg:shortKeyID */
"Could not export key %@" = "Could not export key %@";

/* no comment */
"Could not read file" = "Could not read file";

/* no comment */
"Could not write to directory" = "Could not write to directory";

/* arg1:successCount arg2:totalCount */
"Decrypted %1$u of %2$u files" = "Decrypted %1$u of %2$u files";

Expand Down
163 changes: 123 additions & 40 deletions Source/GPGServices.m
Expand Up @@ -16,7 +16,10 @@
#import "ServiceWorker.h"
#import "ServiceWorkerDelegate.h"
#import "ServiceWrappedArgs.h"
#import "GPGTempFile.h"

#import "Libmacgpg/GPGFileStream.h"
#import "Libmacgpg/GPGMemoryStream.h"
#import "ZipOperation.h"
#import "ZipKit/ZKArchive.h"
#import "NSPredicate+negate.h"
Expand Down Expand Up @@ -665,7 +668,7 @@ - (NSString*)detachedSignFile:(NSString*)file withKeys:(NSArray*)keys {
for(GPGKey* k in keys)
[ctx addSignerKey:[k description]];

NSData* dataToSign = nil;
GPGStream* dataToSign = nil;

if([[self isDirectoryPredicate] evaluateWithObject:file]) {
ZipOperation* zipOperation = [[[ZipOperation alloc] init] autorelease];
Expand All @@ -677,21 +680,56 @@ - (NSString*)detachedSignFile:(NSString*)file withKeys:(NSArray*)keys {
if([zipOperation.zipData writeToFile:file atomically:YES] == NO)
return nil;

dataToSign = [[[NSData alloc] initWithContentsOfFile:file] autorelease];
dataToSign = [GPGFileStream fileStreamForReadingAtPath:file];
} else {
dataToSign = [[[NSData alloc] initWithContentsOfFile:file] autorelease];
dataToSign = [GPGFileStream fileStreamForReadingAtPath:file];
}

NSData* signData = [ctx processData:dataToSign withEncryptSignMode:GPGDetachedSign recipients:nil hiddenRecipients:nil];
if (!dataToSign) {
[self displayOperationFailedNotificationWithTitle:NSLocalizedString(@"Could not read file", nil)
message:file];
return nil;
}

// write to a temporary location in the target directory
static NSString * const tempTemplate = @"_gpgXXX.sig";
static NSUInteger const suffixLen = 4;
NSError *error = nil;
GPGTempFile *tempFile = [GPGTempFile tempFileForTemplate:
[file stringByAppendingString:tempTemplate]
suffixLen:suffixLen error:&error];
if (error) {
[self displayOperationFailedNotificationWithTitle:NSLocalizedString(@"Could not write to directory", nil)
message:[file stringByDeletingLastPathComponent]];
return nil;
}

GPGFileStream *output = [GPGFileStream fileStreamForWritingAtPath:tempFile.fileName];
[ctx processTo:output data:dataToSign withEncryptSignMode:GPGDetachedSign recipients:nil hiddenRecipients:nil];

if (ctx.error)
@throw ctx.error;

NSString* sigFile = [file stringByAppendingPathExtension:@"sig"];
sigFile = [self normalizedAndUniquifiedPathFromPath:sigFile];
[signData writeToFile:sigFile atomically:YES];

return sigFile;
if ([output length]) {
[output close];

NSString* sigFile = [file stringByAppendingPathExtension:@"sig"];
sigFile = [self normalizedAndUniquifiedPathFromPath:sigFile];

error = nil;
[[NSFileManager defaultManager] moveItemAtPath:tempFile.fileName toPath:sigFile error:&error];
if(!error) {
tempFile.shouldDeleteFileOnDealloc = NO;
return sigFile;
}

NSLog(@"error while writing to output: %@", error);
[tempFile deleteFile];
}
else {
[output close];
[tempFile deleteFile];
}
} @catch (GPGException* e) {
if([GrowlApplicationBridge isGrowlRunning]) {//This is in a loop, so only display Growl...
NSString *msg = [NSString stringWithFormat:@"%@\n\n%@", [file lastPathComponent], e];
Expand Down Expand Up @@ -846,7 +884,7 @@ - (void)encryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {

NSFileManager* fmgr = [[[NSFileManager alloc] init] autorelease];

typedef NSData*(^DataProvider)();
typedef GPGStream*(^DataProvider)();
DataProvider dataProvider = nil;

if(files.count == 1) {
Expand All @@ -868,14 +906,14 @@ - (void)encryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
operation.delegate = self;
[operation start];

return operation.zipData;
return [GPGMemoryStream memoryStreamForReading:operation.zipData];
};
} else {
NSNumber* fileSize = [self sizeOfFiles:[NSArray arrayWithObject:file]];
megabytes = [fileSize unsignedLongLongValue] / kBytesInMB;
destination = [file stringByAppendingFormat:@".%@", fileExtension];
dataProvider = ^{
return (NSData*)[NSData dataWithContentsOfFile:file];
return [GPGFileStream fileStreamForReadingAtPath:file];
};
}
} else if(files.count > 1) {
Expand All @@ -889,14 +927,10 @@ - (void)encryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
operation.delegate = self;
[operation start];

return operation.zipData;
return [GPGMemoryStream memoryStreamForReading:operation.zipData];
};
}

//Check if directory is writable and append i+1 if file already exists at destination
destination = [self normalizedAndUniquifiedPathFromPath:destination];

GPGDebugLog(@"destination: %@", destination);
GPGDebugLog(@"fileSize: %@Mb", [NSNumberFormatter localizedStringFromNumber:[NSNumber numberWithDouble:megabytes]
numberStyle:NSNumberFormatterDecimalStyle]);

Expand All @@ -910,18 +944,33 @@ - (void)encryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
GPGController* ctx = [GPGController gpgController];
// Only use armor for single files. otherwise it doesn't make much sense.
ctx.useArmor = useASCII && [destination rangeOfString:@".asc"].location != NSNotFound;
NSData* gpgData = nil;
GPGStream* gpgData = nil;
if(dataProvider != nil)
gpgData = [[[NSData alloc] initWithData:dataProvider()] autorelease];
gpgData = dataProvider();

// write to a temporary location in the target directory
static NSString * const tempTemplate = @"_gpgXXX.gpg";
static NSUInteger const suffixLen = 4;
NSError *error = nil;
GPGTempFile *tempFile = [GPGTempFile tempFileForTemplate:
[destination stringByAppendingString:tempTemplate]
suffixLen:suffixLen error:&error];
if (error) {
[self displayOperationFailedNotificationWithTitle:NSLocalizedString(@"Could not write to directory", nil)
message:[destination stringByDeletingLastPathComponent]];
return;
}

NSData* encrypted = nil;
GPGFileStream *output = [GPGFileStream fileStreamForWritingAtPath:tempFile.fileName];

if(mode == GPGEncryptSign && privateKey != nil)
[ctx addSignerKey:[privateKey description]];
@try{
encrypted = [ctx processData:gpgData
withEncryptSignMode:mode
recipients:validRecipients
hiddenRecipients:nil];
[ctx processTo:output
data:gpgData
withEncryptSignMode:mode
recipients:validRecipients
hiddenRecipients:nil];

// check after a lengthy operation
if (wrappedArgs.worker.amCanceling)
Expand All @@ -940,13 +989,22 @@ - (void)encryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
return;
}

if(encrypted == nil) {
//Check if directory is writable and append i+1 if file already exists at destination
destination = [self normalizedAndUniquifiedPathFromPath:destination];
GPGDebugLog(@"destination: %@", destination);

[output close];
error = nil;
[[NSFileManager defaultManager] moveItemAtPath:tempFile.fileName toPath:destination error:&error];
if (error) {
[tempFile deleteFile];
// We should probably show the file from the exception too.
[self displayOperationFailedNotificationWithTitle:NSLocalizedString(@"Encryption failed", nil)
message:[destination lastPathComponent]];
return;
}
[encrypted writeToFile:destination atomically:YES];

tempFile.shouldDeleteFileOnDealloc = NO;
[self displayOperationFinishedNotificationWithTitle:NSLocalizedString(@"Encryption finished", nil)
message:[destination lastPathComponent]];
}
Expand Down Expand Up @@ -996,23 +1054,48 @@ - (void)decryptFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
@try {
if([fmgr fileExistsAtPath:file isDirectory:&isDirectory] &&
isDirectory == NO) {
NSData* inputData = [[[NSData alloc] initWithContentsOfFile:file] autorelease];
GPGDebugLog(@"inputData.size: %lu", [inputData length]);

NSData* outputData = [ctx decryptData:inputData];
GPGFileStream *input = [GPGFileStream fileStreamForReadingAtPath:file];
GPGDebugLog(@"inputData.size: %llu", [input length]);

// write to a temporary location in the target directory
static NSString * const tempTemplate = @"_gpgXXX.out";
static NSUInteger const suffixLen = 4;
NSError *error = nil;
GPGTempFile *tempFile = [GPGTempFile tempFileForTemplate:
[file stringByAppendingString:tempTemplate]
suffixLen:suffixLen error:&error];
if (error) {
[self displayOperationFailedNotificationWithTitle:
NSLocalizedString(@"Could not write to directory", nil)
message:[file stringByDeletingLastPathComponent]];
return;
}

GPGFileStream *output = [GPGFileStream fileStreamForWritingAtPath:tempFile.fileName];
[ctx decryptTo:output data:input];

// check again after a potentially long operation
if (wrappedArgs.worker.amCanceling)
return;

if (outputData && [outputData length]) {
if ([output length]) {
[output close];

error = nil;
NSString* outputFile = [self normalizedAndUniquifiedPathFromPath:[file stringByDeletingPathExtension]];
NSError* error = nil;
[outputData writeToFile:outputFile options:NSDataWritingAtomic error:&error];
if(error != nil)
[[NSFileManager defaultManager] moveItemAtPath:tempFile.fileName toPath:outputFile error:&error];
if(error != nil) {
NSLog(@"error while writing to output: %@", error);
else
[tempFile deleteFile];
}
else {
tempFile.shouldDeleteFileOnDealloc = NO;
[decryptedFiles addObject:file];
}
}
else {
[output close];
[tempFile deleteFile];
}

if (ctx.error)
Expand Down Expand Up @@ -1155,9 +1238,9 @@ - (void)verifyFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
if([fmgr fileExistsAtPath:signedFile] && [fmgr fileExistsAtPath:signatureFile]) {
@try {
GPGController* ctx = [GPGController gpgController];
NSData* signatureFileData = [[[NSData alloc] initWithContentsOfFile:signatureFile] autorelease];
NSData* signedFileData = [[[NSData alloc] initWithContentsOfFile:signedFile] autorelease];
sigs = [ctx verifySignature:signatureFileData originalData:signedFileData];
GPGFileStream *signatureInput = [GPGFileStream fileStreamForReadingAtPath:signatureFile];
GPGFileStream *originalInput = [GPGFileStream fileStreamForReadingAtPath:signedFile];
sigs = [ctx verifySignatureOf:signatureInput originalData:originalInput];
} @catch (NSException *exception) {
firstException = exception;
sigs = nil;
Expand All @@ -1172,8 +1255,8 @@ - (void)verifyFilesWrapped:(ServiceWrappedArgs *)wrappedArgs {
if(sigs == nil || sigs.count == 0) {
@try {
GPGController* ctx = [GPGController gpgController];
NSData* signedFileData = [[[NSData alloc] initWithContentsOfFile:serviceFile] autorelease];
sigs = [ctx verifySignedData:signedFileData];
GPGFileStream *signedInput = [GPGFileStream fileStreamForReadingAtPath:serviceFile];
sigs = [ctx verifySignatureOf:signedInput originalData:nil];

} @catch (NSException *exception) {
secondException = exception;
Expand Down
28 changes: 28 additions & 0 deletions Source/GPGTempFile.h
@@ -0,0 +1,28 @@
//
// GPGTempFile.h
// GPGServices
//
// Created by Chris Fraire on 5/21/12.
// Copyright (c) 2012 Chris Fraire. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface GPGTempFile : NSObject {
NSString *_filename;
BOOL _shouldDeleteOnDealloc;
BOOL _didDeleteFile;
}

// initialize a temp file using mkstemp
+ (id)tempFileForTemplate:(NSString *)template suffixLen:(NSUInteger)suffixLength error:(NSError **)error;
- (id)initForTemplate:(NSString *)template suffixLen:(NSUInteger)suffixLength error:(NSError **)error;

@property (readonly) NSString *fileName;

// default is YES
@property (assign) BOOL shouldDeleteFileOnDealloc;

- (void)deleteFile;

@end

0 comments on commit 5035fb1

Please sign in to comment.