Skip to content

Commit

Permalink
Merge pull request PeqNP#5 from PeqNP/Buffer
Browse files Browse the repository at this point in the history
Version 1.2.0
  • Loading branch information
PeqNP committed Apr 30, 2014
2 parents 426eae9 + d7af55a commit ab96e27
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 35 deletions.
27 changes: 25 additions & 2 deletions FTPKit Tests/FTPKit_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ - (void)testFtp
{
FTPClient * ftp = [[FTPClient alloc] initWithHost:@"localhost" port:21 username:@"unittest" password:@"unitpass"];

// Sanity. Make sure the root path exists. This should always be true.
BOOL success = [ftp directoryExistsAtPath:@"/"];
XCTAssertTrue(success, @"");

NSArray *contents = [ftp listContentsAtPath:@"/test" showHiddenFiles:YES];
//XCTAssertNil(contents, @"Directory should not exist");
XCTAssertEqual(0, contents.count, @"");
Expand All @@ -56,7 +60,7 @@ - (void)testFtp
NSURL *localUrl = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"ftplib.tgz"];

// Download 'ftplib.tgz'
BOOL success = [ftp downloadFile:@"/ftplib.tgz" to:localUrl.path progress:NULL];
success = [ftp downloadFile:@"/ftplib.tgz" to:localUrl.path progress:NULL];
XCTAssertTrue(success, @"");

// Upload 'ftplib.tgz' as 'copy.tgz'
Expand All @@ -80,7 +84,7 @@ - (void)testFtp
XCTAssertTrue(exists, @"");

exists = [ftp directoryExistsAtPath:@"/badpath"];
XCTAssertTrue(exists, @"");
XCTAssertFalse(exists, @"");

bytes = [ftp fileSizeAtPath:@"/badpath.txt"];
XCTAssertEqual(-1, bytes, @"");
Expand All @@ -107,6 +111,25 @@ - (void)testFtp
success = [ftp createDirectoryAtPath:@"/test/test2"];
XCTAssertTrue(success, @"");

NSString *cwd = [ftp printWorkingDirectory];
XCTAssertTrue([cwd isEqualToString:@"/"], @"");

// Change directory to /test
success = [ftp changeDirectoryToPath:@"/test"];
XCTAssertTrue(success, @"");

/**
Currently the connection is not left open between calls and therefore we
will always be put back to the root directory when each command is sent.
Uncomment this when the same connection is used between commands.
// Make sure we are still in /test.
cwd = [ftp printWorkingDirectory];
NSLog(@"cwd is %@", cwd);
XCTAssertTrue([cwd isEqualToString:@"/test"], @"");
*/

// List contents of 'test'
contents = [ftp listContentsAtPath:@"/test" showHiddenFiles:YES];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>540164E0-A7B5-4DAB-B0C4-5F108479B2B0</string>
<string>318C0C2B-1164-4EF8-AF86-3CEB547A24B6</string>
<key>IDESourceControlProjectName</key>
<string>FTPKit</string>
<key>IDESourceControlProjectOriginsDictionary</key>
Expand Down
Binary file not shown.
62 changes: 62 additions & 0 deletions FTPKit/FTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
Consider implementing more of the commands specified at:
http://en.wikipedia.org/wiki/List_of_FTP_commands
Currently this creates a new connection to the FTP server for every command
issued. This means the state of the current working directory is NOT kept and.
therefore, some commands are not of use.
*/

#import "FTPHandle.h"
Expand Down Expand Up @@ -414,12 +418,70 @@
*/
- (NSDate *)lastModifiedAtPath:(NSString *)remotePath;

/**
Refer to lastModifiedAtPath:
This adds the ability to perform the operation asynchronously.
@param remotePath Remote path to check
@param success Method called when process succeeds. 'lastModified' is the
last modified time.
@param failure Method called when process fails.
*/
- (void)lastModifiedAtPath:(NSString *)remotePath
success:(void (^)(NSDate *lastModified))success
failure:(void (^)(NSError *error))failure;

/**
Check if a remote directory exists.
Please note that this internally calls [self changeDirectoryToPath:] and does
_not_ change back to the previous directory!
@param remotePath Directory to check
@return YES if the directory exists. NO, otherwise
*/
- (BOOL)directoryExistsAtPath:(NSString *)remotePath;

/**
Refer to directoryExistsAtPath:
This adds the ability to perform the operation asynchronously.
@param remotePath Remote path to check
@param success Method called when process succeeds. 'exists' will be YES if the
directory exists. NO otherwise.
@param failure Method called when process fails.
*/
- (void)directoryExistsAtPath:(NSString *)remotePath
success:(void (^)(BOOL exists))success
failure:(void (^)(NSError *error))failure;

/**
Change the working directory to remotePath.
@note This is currently used ONLY to determine if a directory exists on the
server. The state of the cwd is not saved between commands being issued. This
is because a new connection is created for every command issued.
Therefore, in its current state, it is used in a very limited scope. Eventually
you will be able to issue commands in the cwd. Not right now.
@param remotePath Remote directory path to make current directory.
@return YES if the directory was successfully changed.
*/
- (BOOL)changeDirectoryToPath:(NSString *)remotePath;

/**
Returns the current working directory.
@note Currently this will always return the root path. This is because the
lib creates a new connection for every command issued to the server -- and
therefore the command will always being in the root path when issuing the
command.
@return The current working directory.
*/
- (NSString *)printWorkingDirectory;

@end
150 changes: 121 additions & 29 deletions FTPKit/FTPClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ - (NSArray *)parseListData:(NSData *)data handle:(FTPHandle *)handle showHiddent
*/
- (void)failedWithMessage:(NSString *)message;

/**
Convenience method that wraps failure(error) in dispatch_async(main_queue)
and ensures that the error is copied before sending back to callee -- to ensure
it doesn't get nil'ed out by the next command before the callee has a chance
to read the error.
*/
- (void)returnFailure:(void (^)(NSError *error))failure;

@end

@implementation FTPClient
Expand Down Expand Up @@ -97,8 +105,7 @@ - (long long int)fileSizeAtPath:(NSString *)path
int stat = FtpSize(cPath, &bytes, FTPLIB_BINARY, conn);
FtpQuit(conn);
if (stat == 0) {
FKLogError(@"SIZE %@", path);
self.lastError = [NSError FTPKitErrorWithCode:451];
FKLogError(@"File most likely does not exist %@", path);
return -1;
}
FKLogDebug(@"%@ bytes %d", path, bytes);
Expand Down Expand Up @@ -139,6 +146,11 @@ - (NSArray *)listContentsAtHandle:(FTPHandle *)handle showHiddenFiles:(BOOL)show
self.lastError = error;
return nil;
}
/**
Please note: If there are no contents in the folder OR if the folder does
not exist data.bytes _will_ be 0. Therefore, you can not use this method to
determine if a directory exists!
*/
[[NSFileManager defaultManager] removeItemAtPath:tmpPath error:&error];
// Log the error, but do not fail.
if (error) {
Expand All @@ -157,9 +169,7 @@ - (void)listContentsAtHandle:(FTPHandle *)handle showHiddenFiles:(BOOL)showHidde
success(contents);
});
} else if (! contents && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -202,9 +212,7 @@ - (void)downloadHandle:(FTPHandle *)handle to:(NSString *)localPath progress:(BO
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -239,9 +247,7 @@ - (void)uploadFile:(NSString *)localPath to:(NSString *)remotePath progress:(BOO
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -281,9 +287,7 @@ - (void)createDirectoryAtHandle:(FTPHandle *)handle success:(void (^)(void))succ
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -337,9 +341,7 @@ - (void)deleteHandle:(FTPHandle *)handle success:(void (^)(void))success failure
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -385,9 +387,7 @@ - (void)chmodHandle:(FTPHandle *)handle toMode:(int)mode success:(void (^)(void)
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -418,9 +418,7 @@ - (void)renamePath:(NSString *)sourcePath to:(NSString *)destPath success:(void
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -453,9 +451,7 @@ - (void)copyPath:(NSString *)sourcePath to:(NSString *)destPath success:(void (^
success();
});
} else if (! ret && failure) {
dispatch_async(dispatch_get_main_queue(), ^{
failure(_lastError);
});
[self returnFailure:failure];
}
});
}
Expand Down Expand Up @@ -600,12 +596,108 @@ - (NSDate *)lastModifiedAtPath:(NSString *)remotePath
return date;
}

- (void)lastModifiedAtPath:(NSString *)remotePath success:(void (^)(NSDate *))success failure:(void (^)(NSError *))failure
{
dispatch_async(_queue, ^{
NSDate *date = [self lastModifiedAtPath:remotePath];
if (! _lastError && success) {
dispatch_async(dispatch_get_main_queue(), ^{
success(date);
});
} else if (_lastError && failure) {
[self returnFailure:failure];
}
});
}

- (BOOL)directoryExistsAtPath:(NSString *)remotePath
{
NSArray *contents = [self listContentsAtPath:remotePath showHiddenFiles:NO];
if (contents)
/**
Test the directory by changing to the directory. If the process succeeds
then the directory exists.
The process is to get the current working directory and change _back_ to
the previous current working directory. There is a possibility that the
second changeDirectoryToPath: may fail! This is really the price we pay
for this command as there is no other accurate way to determine this.
Using listContentsAtPath:showHiddenFiles: will fail as it will return empty
contents even if the directory doesn't exist! So long as the command
_succeeds_ it will return an empty list.
// Get the current working directory. We will change back to this directory
// if necessary.
NSString *cwd = [self printWorkingDirectory];
// No need to continue. We already know the path exists by the fact that we
// are currently _in_ the directory.
if ([cwd isEqualToString:remotePath])
return YES;
return NO;
// Test directory by changing to it.
BOOL success = [self changeDirectoryToPath:remotePath];
// Attempt to change back to the previous directory.
if (success)
[self changeDirectoryToPath:cwd];
return success;
*/

/**
Currently the lib creates a new connection for every command issued.
Therefore, it is unnecessary to change back to the original cwd.
*/
BOOL success = [self changeDirectoryToPath:remotePath];
return success;
}

- (void)directoryExistsAtPath:(NSString *)remotePath success:(void (^)(BOOL))success failure:(void (^)(NSError *))failure
{
dispatch_async(_queue, ^{
BOOL exists = [self directoryExistsAtPath:remotePath];
if (! _lastError && success) {
dispatch_async(dispatch_get_main_queue(), ^{
success(exists);
});
} else if (_lastError && failure) {
[self returnFailure:failure];
}
});
}

- (BOOL)changeDirectoryToPath:(NSString *)remotePath
{
netbuf *conn = [self connect];
if (conn == NULL)
return NO;
const char *cPath = [remotePath cStringUsingEncoding:NSUTF8StringEncoding];
int stat = FtpChdir(cPath, conn);
FtpQuit(conn);
if (stat == 0) {
self.lastError = [NSError FTPKitErrorWithCode:450];
return NO;
}
return YES;
}

- (NSString *)printWorkingDirectory
{
netbuf *conn = [self connect];
if (conn == NULL)
return NO;
char cPath[kFTPKitTempBufferSize];
int stat = FtpPwd(cPath, kFTPKitTempBufferSize, conn);
FtpQuit(conn);
if (stat == 0) {
self.lastError = [NSError FTPKitErrorWithCode:450];
return NO;
}
return [NSString stringWithCString:cPath encoding:NSUTF8StringEncoding];
}

- (void)returnFailure:(void (^)(NSError *))failure
{
NSError *error = [_lastError copy];
dispatch_async(dispatch_get_main_queue(), ^{
failure(error);
});
}

@end
2 changes: 1 addition & 1 deletion Libraries/include/ftplib/src/ftplib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,7 @@ GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl)
int l = max;
char *b = path;
char *s;
if (!FtpSendCmd("PWD",'2',nControl))
if (!FtpSendCmd("PWD", '2', nControl))
return 0;
s = strchr(nControl->response, '"');
if (s == NULL)
Expand Down
Loading

0 comments on commit ab96e27

Please sign in to comment.