Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Use GPGStream for file operations

  • Loading branch information...
commit 5035fb13d9aad9a5f21783f77bc5310b69963f94 1 parent acbc74c
@idodeclare idodeclare authored
View
6 GPGServices.xcodeproj/project.pbxproj
@@ -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 */; };
@@ -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>"; };
@@ -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 */,
@@ -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;
};
View
6 Resources/en.lproj/Localizable.strings
@@ -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";
View
163 Source/GPGServices.m
@@ -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"
@@ -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];
@@ -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];
@@ -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) {
@@ -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) {
@@ -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]);
@@ -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)
@@ -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]];
}
@@ -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)
@@ -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;
@@ -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;
View
28 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
View
71 Source/GPGTempFile.m
@@ -0,0 +1,71 @@
+//
+// GPGTempFile.m
+// GPGServices
+//
+// Created by Chris Fraire on 5/21/12.
+// Copyright (c) 2012 Chris Fraire. All rights reserved.
+//
+
+#import "GPGTempFile.h"
+
+@implementation GPGTempFile
+
+@synthesize fileName = _filename;
+@synthesize shouldDeleteFileOnDealloc = _shouldDeleteOnDealloc;
+
+- (void)dealloc
+{
+ if (_shouldDeleteOnDealloc && !_didDeleteFile)
+ [self deleteFile];
+ [_filename release];
+ [super dealloc];
+}
+
++ (id)tempFileForTemplate:(NSString *)template suffixLen:(NSUInteger)suffixLength error:(NSError **)error {
+ return [[[self alloc] initForTemplate:template suffixLen:suffixLength error:error] autorelease];
+}
+
+- (id)initForTemplate:(NSString *)template suffixLen:(NSUInteger)suffixLength error:(NSError **)error
+{
+ if (self = [super init]) {
+ // converting the template to writeable UTF8 for the libc functions
+ const char *utfRoTemplate = [template UTF8String];
+ size_t utfLength = strlen(utfRoTemplate);
+
+ char utfTemplate[utfLength + 1];
+ strncpy(utfTemplate, utfRoTemplate, utfLength);
+ utfTemplate[utfLength] = '\0';
+
+ // convert the suffix as well to get a computed suffix length
+ int utfSuffixLength = 0;
+ if (suffixLength > 0) {
+ NSString *suffix = [template substringFromIndex:[template length] - suffixLength];
+ const char *utfSuffix = [suffix UTF8String];
+ utfSuffixLength = strlen(utfSuffix);
+ }
+
+ int rc = mkstemps(utfTemplate, utfSuffixLength);
+ if (rc == -1) {
+ if (error)
+ *error = [NSError errorWithDomain:@"libc" code:rc userInfo:nil];
+ _didDeleteFile = YES; // treat as already gone
+ }
+ else {
+ _filename = [[NSString stringWithUTF8String:utfTemplate] retain];
+ }
+
+ _shouldDeleteOnDealloc = YES;
+ }
+
+ return self;
+}
+
+- (void)deleteFile
+{
+ NSError *error = nil;
+ [[NSFileManager defaultManager] removeItemAtPath:_filename error:&error];
+ if (error == nil)
+ _didDeleteFile = YES;
+}
+
+@end
Please sign in to comment.
Something went wrong with that request. Please try again.