diff --git a/Changelog b/Changelog index b3f2c6e..d0b6bf5 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,11 @@ +Weid, Nov 12 2014 -- v1.3.1 +======================================== +Fixed ++ Server error messages are now being captured and returned, rather than +guessing what the error is. + Sun, May 25 2014 -- v1.3.0 ======================================== -New: +New + It is no longer necessary to URL encode paths before making calls to library; as all paths are now URL encoded internally. diff --git a/FTPKit.xcodeproj/project.pbxproj b/FTPKit.xcodeproj/project.pbxproj index 03a3e49..a6ddf87 100644 --- a/FTPKit.xcodeproj/project.pbxproj +++ b/FTPKit.xcodeproj/project.pbxproj @@ -456,6 +456,7 @@ F27BA1F21802FE7E00584A9E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = NO; CLANG_ENABLE_OBJC_ARC = YES; CURRENT_PROJECT_VERSION = 1.0.0; DEAD_CODE_STRIPPING = YES; @@ -483,6 +484,7 @@ F27BA1F31802FE7E00584A9E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = NO; CLANG_ENABLE_OBJC_ARC = YES; CURRENT_PROJECT_VERSION = 1.0.0; DEAD_CODE_STRIPPING = YES; diff --git a/FTPKit/Categories/NSError+Additions.h b/FTPKit/Categories/NSError+Additions.h index ef71b36..2eeca29 100644 --- a/FTPKit/Categories/NSError+Additions.h +++ b/FTPKit/Categories/NSError+Additions.h @@ -11,4 +11,6 @@ extern NSString *const FTPErrorDomain; */ + (NSError *)FTPKitErrorWithCode:(int)errorCode; ++ (NSError *)FTPKitErrorWithResponse:(NSString *)response; + @end diff --git a/FTPKit/Categories/NSError+Additions.m b/FTPKit/Categories/NSError+Additions.m index 8630e6a..27166f2 100644 --- a/FTPKit/Categories/NSError+Additions.m +++ b/FTPKit/Categories/NSError+Additions.m @@ -1,4 +1,5 @@ #import "NSError+Additions.h" +#import "NSString+Additions.h" NSString *const FTPErrorDomain = @"FTPKit"; @@ -113,4 +114,20 @@ + (NSError *)FTPKitErrorWithCode:(int)errorCode return [[NSError alloc] initWithDomain:FTPErrorDomain code:errorCode userInfo:userInfo]; } ++ (NSError *)FTPKitErrorWithResponse:(NSString *)response +{ + // Extract the code and message from the reponse message. + // Ex: '500 Server error' + NSMutableArray *components = [[response componentsSeparatedByString:@" "] mutableCopy]; + NSInteger code = 500; + if ([components[0] isIntegerValue]) { + code = [components[0] integerValue]; + [components removeObjectAtIndex:0]; + } + NSString *message = [components componentsJoinedByString:@" "]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:message + forKey:NSLocalizedDescriptionKey]; + return [[NSError alloc] initWithDomain:FTPErrorDomain code:code userInfo:userInfo]; +} + @end diff --git a/FTPKit/Categories/NSString+Additions.h b/FTPKit/Categories/NSString+Additions.h index ee52078..9d32243 100644 --- a/FTPKit/Categories/NSString+Additions.h +++ b/FTPKit/Categories/NSString+Additions.h @@ -20,4 +20,6 @@ - (NSString *)FTPKitURLEncodedString; - (NSString *)FTPKitURLDecodedString; +- (BOOL)isIntegerValue; + @end \ No newline at end of file diff --git a/FTPKit/Categories/NSString+Additions.m b/FTPKit/Categories/NSString+Additions.m index fcbd1f2..f30ed61 100644 --- a/FTPKit/Categories/NSString+Additions.m +++ b/FTPKit/Categories/NSString+Additions.m @@ -24,4 +24,13 @@ - (NSString *)FTPKitURLDecodedString return [NSString FTPKitURLDecodeString:self]; } +- (BOOL)isIntegerValue +{ + NSScanner *scanner = [NSScanner scannerWithString:self]; + if ([scanner scanInteger:NULL]) { + return [scanner isAtEnd]; + } + return NO; +} + @end diff --git a/FTPKit/FTPClient.m b/FTPKit/FTPClient.m index 2972f21..ea0eb21 100644 --- a/FTPKit/FTPClient.m +++ b/FTPKit/FTPClient.m @@ -93,7 +93,7 @@ - (instancetype)initWithCredentials:(FTPCredentials *)aLocation self = [super init]; if (self) { self.credentials = aLocation; - self.queue = dispatch_queue_create("NMSFTPQueue", DISPATCH_QUEUE_SERIAL); + self.queue = dispatch_queue_create("com.upstart-illustration-llc.FTPKitQueue", DISPATCH_QUEUE_SERIAL); } return self; } @@ -147,10 +147,10 @@ - (NSArray *)listContentsAtHandle:(FTPHandle *)handle showHiddenFiles:(BOOL)show NSString *tmpPath = [self temporaryUrl]; const char *output = [tmpPath cStringUsingEncoding:NSUTF8StringEncoding]; int stat = FtpDir(output, path, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return nil; } NSError *error = nil; @@ -207,11 +207,11 @@ - (BOOL)downloadHandle:(FTPHandle *)handle to:(NSString *)localPath progress:(BO const char *path = [[self urlEncode:handle.path] cStringUsingEncoding:NSUTF8StringEncoding]; // @todo Send w/ appropriate mode. FTPLIB_ASCII | FTPLIB_BINARY int stat = FtpGet(output, path, FTPLIB_BINARY, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; // @todo Use 'progress' block. FtpQuit(conn); if (stat == 0) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -241,12 +241,12 @@ - (BOOL)uploadFile:(NSString *)localPath to:(NSString *)remotePath progress:(BOO // @todo Send w/ appropriate mode. FTPLIB_ASCII | FTPLIB_BINARY int stat = FtpPut(input, path, FTPLIB_BINARY, conn); // @todo Use 'progress' block. + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - // @todo Why? - // In my experience this usually fails because the user does not have - // permissions to access the file. - self.lastError = [NSError FTPKitErrorWithCode:451]; + // Invalid path, wrong permissions, etc. Make sure that permissions are + // set corectly on the path AND the path of the initialPath is correct. + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -283,10 +283,10 @@ - (BOOL)createDirectoryAtHandle:(FTPHandle *)handle return NO; const char *path = [handle.path cStringUsingEncoding:NSUTF8StringEncoding]; int stat = FtpMkdir(path, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -337,10 +337,10 @@ - (BOOL)deleteHandle:(FTPHandle *)handle stat = FtpRmdir(path, conn); else stat = FtpDelete(path, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -383,10 +383,10 @@ - (BOOL)chmodHandle:(FTPHandle *)handle toMode:(int)mode if (conn == NULL) return NO; BOOL success = [self sendCommand:command conn:conn]; + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (! success) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -416,10 +416,10 @@ - (BOOL)renamePath:(NSString *)sourcePath to:(NSString *)destPath // it is, the filename will include the percent escaping! const char *dst = [destPath cStringUsingEncoding:NSUTF8StringEncoding]; int stat = FtpRename(src, dst, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - // @todo Why? - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -490,7 +490,8 @@ - (netbuf *)connect } stat = FtpLogin(user, pass, conn); if (stat == 0) { - self.lastError = [NSError FTPKitErrorWithCode:430]; + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; FtpQuit(conn); return NULL; } @@ -501,8 +502,8 @@ - (BOOL)sendCommand:(NSString *)command conn:(netbuf *)conn { const char *cmd = [command cStringUsingEncoding:NSUTF8StringEncoding]; if (!FtpSendCmd(cmd, '2', conn)) { - // Could also be 451 - self.lastError = [NSError FTPKitErrorWithCode:550]; + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -598,9 +599,10 @@ - (NSDate *)lastModifiedAtPath:(NSString *)remotePath // MDTM does not work with folders. It is meant to be used only for types // of files that can be downloaded using the RETR command. int stat = FtpModDate(cPath, dt, kFTPKitRequestBufferSize, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - self.lastError = [NSError FTPKitErrorWithCode:451]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return nil; } NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; @@ -692,9 +694,10 @@ - (BOOL)changeDirectoryToPath:(NSString *)remotePath return NO; const char *cPath = [remotePath cStringUsingEncoding:NSUTF8StringEncoding]; int stat = FtpChdir(cPath, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - self.lastError = [NSError FTPKitErrorWithCode:450]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return NO; } return YES; @@ -707,9 +710,10 @@ - (NSString *)printWorkingDirectory return nil; char cPath[kFTPKitTempBufferSize]; int stat = FtpPwd(cPath, kFTPKitTempBufferSize, conn); + NSString *response = [NSString stringWithCString:FtpLastResponse(conn) encoding:NSUTF8StringEncoding]; FtpQuit(conn); if (stat == 0) { - self.lastError = [NSError FTPKitErrorWithCode:450]; + self.lastError = [NSError FTPKitErrorWithResponse:response]; return nil; } return [NSString stringWithCString:cPath encoding:NSUTF8StringEncoding]; diff --git a/Libraries/include/ftplib/src/ftplib.c b/Libraries/include/ftplib/src/ftplib.c index c2318ff..de617c1 100644 --- a/Libraries/include/ftplib/src/ftplib.c +++ b/Libraries/include/ftplib/src/ftplib.c @@ -100,7 +100,7 @@ struct NetBuf { static char *version = "ftplib Release 4.0 07-Jun-2013, copyright 1996-2003, 2013 Thomas Pfau"; -GLOBALDEF int ftplib_debug = 0; +GLOBALDEF int ftplib_debug = 3; #if defined(NEED_STRDUP) /* @@ -581,7 +581,16 @@ GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) { int sControl; struct sockaddr_in sin; - int on=1; + int on = 1; + /** Timeout after 20 seconds. + + The link below provides instruction on using select() to monitor + connections after a given timeout. + http://stackoverflow.com/questions/4181784/how-to-set-socket-timeout-in-c-when-making-multiple-connections + + struct timeval timeout; + timeout.tv_sec = 20; + timeout.tv_usec = 0; */ netbuf *ctrl; char *lhost; char *pnum; @@ -684,10 +693,19 @@ GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) { if (ftplib_debug) - perror("setsockopt"); + perror("setsockopt SO_REUSEADDR"); net_close(sControl); return 0; } + /** This does not work with the connect() method. + if (setsockopt(sControl, SOL_SOCKET, SO_SNDTIMEO, + (char *)&timeout, sizeof(timeout)) < 0) + { + if (ftplib_debug) + perror("setsockopt SO_SNDTIMEO"); + net_close(sControl); + return 0; + } */ if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) { if (ftplib_debug) @@ -807,7 +825,7 @@ GLOBALDEF int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) fprintf(stderr,"%s\n",cmd); if ((strlen(cmd) + 3) > sizeof(buf)) return 0; - sprintf(buf,"%s\r\n",cmd); + sprintf(buf,"%s\r\n", cmd); if (net_write(nControl->handle,buf,strlen(buf)) <= 0) { if (ftplib_debug)