Repository: kolyvan/kxsmb Branch: master Commit: 30fc7b8d043c Files: 20 Total size: 174.6 KB Directory structure: gitextract_3ps15m81/ ├── .gitignore ├── KxSMB/ │ └── KxSMB-Prefix.pch ├── KxSMBProvider.h ├── KxSMBProvider.m ├── KxSMBSample/ │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── FileViewController.h │ ├── FileViewController.m │ ├── KxSMBSample-Info.plist │ ├── KxSMBSample-Prefix.pch │ ├── SmbAuthViewController.h │ ├── SmbAuthViewController.m │ ├── TreeViewController.h │ ├── TreeViewController.m │ ├── en.lproj/ │ │ └── InfoPlist.strings │ └── main.m ├── KxSMBSample.xcodeproj/ │ └── project.pbxproj ├── LICENSE ├── Rakefile └── readme.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ## xcode specific build/* *.pbxuser *.mode2v3 *.mode1v3 *.perspective *.perspectivev3 *~.nib ## ignore private workspace stuff added by Xcode4 xcuserdata project.xcworkspace *.xcuserdata ## generic files to ignore *~ *.lock *.DS_Store *.swp *.out ## my tmp/* libs/* samba/* plan ================================================ FILE: KxSMB/KxSMB-Prefix.pch ================================================ // // Prefix header for all source files of the 'KxSMB' target in the 'KxSMB' project // #ifdef __OBJC__ #import #endif ================================================ FILE: KxSMBProvider.h ================================================ // // KxSambaProvider.h // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 28.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import extern NSString * const _Nonnull KxSMBErrorDomain; typedef enum { KxSMBErrorUnknown, KxSMBErrorInvalidArg, KxSMBErrorInvalidProtocol, KxSMBErrorOutOfMemory, KxSMBErrorAccessDenied, KxSMBErrorInvalidPath, KxSMBErrorPathIsNotDir, KxSMBErrorPathIsDir, KxSMBErrorWorkgroupNotFound, KxSMBErrorShareDoesNotExist, KxSMBErrorItemAlreadyExists, KxSMBErrorDirNotEmpty, KxSMBErrorFileIO, KxSMBErrorConnRefused, KxSMBErrorOpNotPermited, } KxSMBError; typedef enum { KxSMBItemTypeUnknown, KxSMBItemTypeWorkgroup, KxSMBItemTypeServer, KxSMBItemTypeFileShare, KxSMBItemTypePrinter, KxSMBItemTypeComms, KxSMBItemTypeIPC, KxSMBItemTypeDir, KxSMBItemTypeFile, KxSMBItemTypeLink, } KxSMBItemType; @class KxSMBItem; @class KxSMBAuth; typedef void (^KxSMBBlock)(id _Nullable result); typedef void (^KxSMBBlockProgress)(KxSMBItem * _Nonnull item, long transferred, BOOL * _Nonnull stop); @interface KxSMBItemStat : NSObject @property(readonly, nonatomic, strong, nonnull) NSDate *lastModified; @property(readonly, nonatomic, strong, nonnull) NSDate *lastAccess; @property(readonly, nonatomic, strong, nonnull) NSDate *creationTime; @property(readonly, nonatomic) SInt64 size; @property(readonly, nonatomic) UInt16 mode; @end @interface KxSMBItem : NSObject @property(readonly, nonatomic) KxSMBItemType type; @property(readonly, nonatomic, strong, nonnull) NSString *path; @property(readonly, nonatomic, strong, nonnull) KxSMBItemStat *stat; @property(readonly, nonatomic, strong, nullable) KxSMBAuth *auth; @end @class KxSMBItemFile; @interface KxSMBItemTree : KxSMBItem - (void) fetchItems:(nullable KxSMBBlock)block; - (nullable id) fetchItems; - (void) createFileWithName:(nonnull NSString *)name overwrite:(BOOL)overwrite block:(nonnull KxSMBBlock)block; - (nullable id) createFileWithName:(nonnull NSString *)name overwrite:(BOOL)overwrite; - (void) removeWithName:(nonnull NSString *)name block:(nonnull KxSMBBlock)block; - (nullable id) removeWithName:(nonnull NSString *)name; @end @interface KxSMBItemFile : KxSMBItem - (void) close; - (void)readDataOfLength:(NSUInteger)length block:(nonnull KxSMBBlock)block; - (nullable id)readDataOfLength:(NSUInteger)length; - (void)readDataToEndOfFile:(nonnull KxSMBBlock)block; - (nullable id)readDataToEndOfFile; - (void)seekToFileOffset:(off_t)offset whence:(NSInteger)whence block:(nonnull KxSMBBlock)block; - (nullable id)seekToFileOffset:(off_t)offset whence:(NSInteger)whence; - (void)writeData:(nonnull NSData *)data block:(nonnull KxSMBBlock)block; - (nullable id)writeData:(nonnull NSData *)data; @end @interface KxSMBAuth : NSObject @property (readwrite, nonatomic, strong, nullable) NSString *workgroup; @property (readwrite, nonatomic, strong, nullable) NSString *username; @property (readwrite, nonatomic, strong, nullable) NSString *password; + (nullable instancetype) smbAuthWorkgroup:(nullable NSString *)workgroup username:(nullable NSString *)username password:(nullable NSString *)password; @end @protocol KxSMBProviderDelegate - (nullable KxSMBAuth *) smbRequestAuthServer:(nonnull NSString *)server share:(nonnull NSString *)share workgroup:(nonnull NSString *)workgroup username:(nonnull NSString *)username; @end // smbc_share_mode typedef NS_ENUM(NSUInteger, KxSMBConfigShareMode) { KxSMBConfigShareModeDenyDOS = 0, KxSMBConfigShareModeDenyAll = 1, KxSMBConfigShareModeDenyWrite = 2, KxSMBConfigShareModeDenyRead = 3, KxSMBConfigShareModeDenyNone = 4, KxSMBConfigShareModeDenyFCB = 7, }; // smbc_smb_encrypt_level typedef NS_ENUM(NSUInteger, KxSMBConfigEncryptLevel) { KxSMBConfigEncryptLevelNone = 0, KxSMBConfigEncryptLevelRequest = 1, KxSMBConfigEncryptLevelRequire = 2, }; @interface KxSMBConfig : NSObject @property (readwrite, nonatomic) NSUInteger timeout; @property (readwrite, nonatomic) NSUInteger debugLevel; @property (readwrite, nonatomic) BOOL debugToStderr; @property (readwrite, nonatomic) BOOL fullTimeNames; @property (readwrite, nonatomic) KxSMBConfigShareMode shareMode; @property (readwrite, nonatomic) KxSMBConfigEncryptLevel encryptionLevel; @property (readwrite, nonatomic) BOOL caseSensitive; @property (readwrite, nonatomic) NSUInteger browseMaxLmbCount; @property (readwrite, nonatomic) BOOL urlEncodeReaddirEntries; @property (readwrite, nonatomic) BOOL oneSharePerServer; @property (readwrite, nonatomic) BOOL useKerberos; @property (readwrite, nonatomic) BOOL fallbackAfterKerberos; @property (readwrite, nonatomic) BOOL noAutoAnonymousLogin; @property (readwrite, nonatomic) BOOL useCCache; @property (readwrite, nonatomic) BOOL useNTHash; @property (readwrite, nonatomic, strong, nullable) NSString *netbiosName; @property (readwrite, nonatomic, strong, nullable) NSString *workgroup; @property (readwrite, nonatomic, strong, nullable) NSString *username; @end @interface KxSMBProvider : NSObject @property (readwrite, nonatomic, weak) id delegate; @property (readwrite, nonatomic, strong, nonnull) KxSMBConfig *config; @property (readwrite, nonatomic, strong, nullable) dispatch_queue_t completionQueue; + (nullable instancetype) sharedSmbProvider; - (void) fetchAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (void) fetchAtPath:(nonnull NSString *)path expandDir:(BOOL)expandDir auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (nullable id) fetchAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth; - (nullable id) fetchAtPath:(nonnull NSString *)path expandDir:(BOOL)expandDir auth:(nullable KxSMBAuth *)auth; - (void) createFileAtPath:(nonnull NSString *)path overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (nullable id) createFileAtPath:(nonnull NSString *)path overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth; - (void) createFolderAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (nullable id) createFolderAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth; - (void) removeAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (nullable id) removeAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth; - (void) copySMBPath:(nonnull NSString *)smbPath localPath:(nonnull NSString *)localPath overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (void) copyLocalPath:(nonnull NSString *)localPath smbPath:(nonnull NSString *)smbPath overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (void) copySMBPath:(nonnull NSString *)smbPath localPath:(nonnull NSString *)localPath overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth progress:(nullable KxSMBBlockProgress)progress block:(nonnull KxSMBBlock)block; - (void) copyLocalPath:(nonnull NSString *)localPath smbPath:(nonnull NSString *)smbPath overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth progress:(nullable KxSMBBlockProgress)progress block:(nonnull KxSMBBlock)block; - (void) copyFromPath:(nonnull NSString *)oldPath toPath:(nonnull NSString *)newPath overwrite:(BOOL)overwrite auth:(nullable KxSMBAuth *)auth progress:(nullable KxSMBBlockProgress)progress block:(nonnull KxSMBBlock)block; - (void) removeFolderAtPath:(nonnull NSString *)path auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; - (void) renameAtPath:(nonnull NSString *)oldPath newPath:(nonnull NSString *)newPath auth:(nullable KxSMBAuth *)auth block:(nonnull KxSMBBlock)block; // without auth (compatible) - (void) fetchAtPath:(nonnull NSString *)path block:(nullable KxSMBBlock)block; - (nullable id) fetchAtPath:(nonnull NSString *)path; - (void) createFileAtPath:(nonnull NSString *)path overwrite:(BOOL)overwrite block:(nonnull KxSMBBlock)block; - (nullable id) createFileAtPath:(nonnull NSString *)path overwrite:(BOOL)overwrite; - (void) createFolderAtPath:(nonnull NSString *)path block:(nonnull KxSMBBlock)block; - (nullable id) createFolderAtPath:(nonnull NSString *)path; - (void) removeAtPath:(nonnull NSString *)path block:(nonnull KxSMBBlock)block; - (nullable id) removeAtPath:(nonnull NSString *)path; - (void) copySMBPath:(nonnull NSString *)smbPath localPath:(nonnull NSString *)localPath overwrite:(BOOL)overwrite block:(nonnull KxSMBBlock)block; - (void) copyLocalPath:(nonnull NSString *)localPath smbPath:(nonnull NSString *)smbPath overwrite:(BOOL)overwrite block:(nonnull KxSMBBlock)block; - (void) copySMBPath:(nonnull NSString *)smbPath localPath:(nonnull NSString *)localPath overwrite:(BOOL)overwrite progress:(nullable KxSMBBlockProgress)progress block:(nonnull KxSMBBlock)block; - (void) copyLocalPath:(nonnull NSString *)localPath smbPath:(nonnull NSString *)smbPath overwrite:(BOOL)overwrite progress:(nullable KxSMBBlockProgress)progress block:(nonnull KxSMBBlock)block; - (void) removeFolderAtPath:(nonnull NSString *)path block:(nonnull KxSMBBlock)block; - (void) renameAtPath:(nonnull NSString *)oldPath newPath:(nonnull NSString *)newPath block:(nonnull KxSMBBlock)block; @end @interface NSString (KxSMB) - (nonnull NSString *) stringByAppendingSMBPathComponent:(nonnull NSString *)aString; @end ================================================ FILE: KxSMBProvider.m ================================================ // // KxSambaProvider.m // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 28.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "KxSMBProvider.h" #import "libsmbclient.h" #import "talloc_stack.h" /////////////////////////////////////////////////////////////////////////////// NSString * const KxSMBErrorDomain = @"ru.kolyvan.KxSMB"; static NSString * KxSMBErrorMessage (KxSMBError errorCode) { switch (errorCode) { case KxSMBErrorUnknown: return NSLocalizedString(@"SMB Error", nil); case KxSMBErrorInvalidArg: return NSLocalizedString(@"SMB Invalid argument", nil); case KxSMBErrorInvalidProtocol: return NSLocalizedString(@"SMB Invalid protocol", nil); case KxSMBErrorOutOfMemory: return NSLocalizedString(@"SMB Out of memory", nil); case KxSMBErrorAccessDenied: return NSLocalizedString(@"SMB Access Denied", nil); case KxSMBErrorInvalidPath: return NSLocalizedString(@"SMB No such file or directory", nil); case KxSMBErrorPathIsNotDir: return NSLocalizedString(@"SMB Not a directory", nil); case KxSMBErrorPathIsDir: return NSLocalizedString(@"SMB Is a directory", nil); case KxSMBErrorWorkgroupNotFound: return NSLocalizedString(@"SMB Workgroup not found", nil); case KxSMBErrorShareDoesNotExist: return NSLocalizedString(@"SMB Share does not exist", nil); case KxSMBErrorItemAlreadyExists: return NSLocalizedString(@"SMB Item already exists", nil); case KxSMBErrorDirNotEmpty: return NSLocalizedString(@"SMB Directory not empty", nil); case KxSMBErrorFileIO: return NSLocalizedString(@"SMB File I/O failure", nil); case KxSMBErrorConnRefused: return NSLocalizedString(@"SMB Connection refused", nil); case KxSMBErrorOpNotPermited: return NSLocalizedString(@"SMB Operation not permitted", nil); } } static NSError * mkKxSMBError(KxSMBError error, NSString *format, ...) { NSDictionary *userInfo = nil; NSString *reason = nil; if (format) { va_list args; va_start(args, format); reason = [[NSString alloc] initWithFormat:format arguments:args]; va_end(args); } if (reason) { userInfo = @{ NSLocalizedDescriptionKey : KxSMBErrorMessage(error), NSLocalizedFailureReasonErrorKey : reason }; } else { userInfo = @{ NSLocalizedDescriptionKey : KxSMBErrorMessage(error) }; } return [NSError errorWithDomain:KxSMBErrorDomain code:error userInfo:userInfo]; } static KxSMBError errnoToSMBErr(int err) { switch (err) { case EINVAL: return KxSMBErrorInvalidArg; case ENOMEM: return KxSMBErrorOutOfMemory; case EACCES: return KxSMBErrorAccessDenied; case ENOENT: return KxSMBErrorInvalidPath; case ENOTDIR: return KxSMBErrorPathIsNotDir; case EISDIR: return KxSMBErrorPathIsDir; case EPERM: return KxSMBErrorOpNotPermited; case ENODEV: return KxSMBErrorShareDoesNotExist; case EEXIST: return KxSMBErrorItemAlreadyExists; case ENOTEMPTY: return KxSMBErrorDirNotEmpty; case ECONNREFUSED: return KxSMBErrorConnRefused; default: return KxSMBErrorUnknown; } } /////////////////////////////////////////////////////////////////////////////// @implementation KxSMBAuth + (instancetype) smbAuthWorkgroup:(NSString *)workgroup username:(NSString *)username password:(NSString *)password { KxSMBAuth *auth = [[KxSMBAuth alloc] init]; auth.workgroup = workgroup; auth.username = username; auth.password = password; return auth; } @end /////////////////////////////////////////////////////////////////////////////// @interface KxSMBItemStat () @property(readwrite, nonatomic, strong) NSDate *lastModified; @property(readwrite, nonatomic, strong) NSDate *lastAccess; @property(readwrite, nonatomic, strong) NSDate *creationTime; @property(readwrite, nonatomic) SInt64 size; @property(readwrite, nonatomic) UInt16 mode; @end @implementation KxSMBItemStat @end @implementation KxSMBItem - (id) initWithType:(KxSMBItemType) type path:(NSString *) path stat:(KxSMBItemStat *)stat auth:(KxSMBAuth *)auth { self = [super init]; if (self) { _type = type; _path = path; _stat = stat; _auth = auth; } return self; } - (NSString *) description { NSString *stype = @""; switch (_type) { case KxSMBItemTypeUnknown: stype = @"?"; break; case KxSMBItemTypeWorkgroup: stype = @"group"; break; case KxSMBItemTypeServer: stype = @"server"; break; case KxSMBItemTypeFileShare: stype = @"fileshare"; break; case KxSMBItemTypePrinter: stype = @"printer"; break; case KxSMBItemTypeComms: stype = @"comms"; break; case KxSMBItemTypeIPC: stype = @"ipc"; break; case KxSMBItemTypeDir: stype = @"dir"; break; case KxSMBItemTypeFile: stype = @"file"; break; case KxSMBItemTypeLink: stype = @"link"; break; } return [NSString stringWithFormat:@"", stype, _path, _stat.size]; } @end /////////////////////////////////////////////////////////////////////////////// static void my_smbc_get_auth_data_fn(const char *srv, const char *shr, char *workgroup, int wglen, char *username, int unlen, char *password, int pwlen); static void my_smbc_get_auth_data_with_context_fn(SMBCCTX *c, const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen); /////////////////////////////////////////////////////////////////////////////// @interface KxSMBItemFile() - (id) createFile:(BOOL)overwrite; @end /////////////////////////////////////////////////////////////////////////////// @implementation KxSMBConfig - (id) init { if ((self = [super init])) { _timeout = 10000; // ms #ifdef DEBUG _debugLevel = 1; #else _debugLevel = 0; #endif _debugToStderr = YES; _fullTimeNames = YES; _shareMode = KxSMBConfigShareModeDenyNone; _encryptionLevel = KxSMBConfigEncryptLevelNone; _caseSensitive = NO; _browseMaxLmbCount = 3; _urlEncodeReaddirEntries = NO; _oneSharePerServer = NO; _useKerberos = NO; _fallbackAfterKerberos = YES; _noAutoAnonymousLogin = NO; _useCCache = NO; _useNTHash = NO; } return self; } - (void) configureSmbContext:(SMBCCTX *)smbContext { smbc_setTimeout(smbContext, (int)_timeout); smbc_setDebug(smbContext, (int)_debugLevel); smbc_setOptionDebugToStderr(smbContext, (smbc_bool)_debugToStderr); smbc_setOptionFullTimeNames(smbContext, (smbc_bool)_fullTimeNames); smbc_setOptionOpenShareMode(smbContext, (smbc_share_mode)_shareMode); smbc_setOptionSmbEncryptionLevel(smbContext, (smbc_smb_encrypt_level)_encryptionLevel); smbc_setOptionCaseSensitive(smbContext, (smbc_bool)_caseSensitive); smbc_setOptionBrowseMaxLmbCount(smbContext, (int)_browseMaxLmbCount); smbc_setOptionUrlEncodeReaddirEntries(smbContext, (smbc_bool)_urlEncodeReaddirEntries); smbc_setOptionOneSharePerServer(smbContext, (smbc_bool)_oneSharePerServer); smbc_setOptionUseKerberos(smbContext, (smbc_bool)_useKerberos); smbc_setOptionFallbackAfterKerberos(smbContext, (smbc_bool)_fallbackAfterKerberos); smbc_setOptionNoAutoAnonymousLogin(smbContext, (smbc_bool)_noAutoAnonymousLogin); smbc_setOptionUseCCache(smbContext, (smbc_bool)_useCCache); smbc_setOptionUseNTHash(smbContext, (smbc_bool)_useNTHash); if (_netbiosName.length) { smbc_setNetbiosName(smbContext, (char *)_netbiosName.UTF8String); } if (_workgroup.length) { smbc_setWorkgroup(smbContext, (char *)_workgroup.UTF8String); } if (_username.length) { smbc_setUser(smbContext, (char *)_username.UTF8String); } } @end /////////////////////////////////////////////////////////////////////////////// static KxSMBProvider *gSmbProvider; @interface KxSMBProvider () @end @implementation KxSMBProvider { dispatch_queue_t _dispatchQueue; } + (instancetype) sharedSmbProvider { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ gSmbProvider = [[KxSMBProvider alloc] init]; }); return gSmbProvider; } - (id) init { NSAssert(!gSmbProvider, @"singleton object"); self = [super init]; if (self) { _config = [KxSMBConfig new]; _dispatchQueue = dispatch_queue_create("KxSMBProvider", DISPATCH_QUEUE_SERIAL); _completionQueue = dispatch_get_main_queue(); } return self; } - (void) dealloc { if (_dispatchQueue) { #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 dispatch_release(_dispatchQueue); #endif _dispatchQueue = NULL; } } #pragma mark - class methods + (SMBCCTX *) openSmbContext:(KxSMBAuth *)auth { //NSParameterAssert(auth); SMBCCTX *smbContext = smbc_new_context(); if (!smbContext) { return NULL; } if (auth) { smbc_setFunctionAuthDataWithContext(smbContext, my_smbc_get_auth_data_with_context_fn); smbc_setOptionUserData(smbContext, (void *)CFBridgingRetain(auth)); } else { smbc_setFunctionAuthData(smbContext, my_smbc_get_auth_data_fn); } KxSMBConfig *cfg = [KxSMBProvider sharedSmbProvider].config; [cfg configureSmbContext:smbContext]; if (!smbc_init_context(smbContext)) { void *userdata = smbc_getOptionUserData(smbContext); if (userdata) { CFBridgingRelease(userdata); } smbc_free_context(smbContext, NO); return NULL; } smbc_set_context(smbContext); return smbContext; } + (void) closeSmbContext:(SMBCCTX *)smbContext { if (smbContext) { void *userdata = smbc_getOptionUserData(smbContext); if (userdata) { CFBridgingRelease(userdata); } // fixes warning: no talloc stackframe at libsmb/cliconnect.c:2637, leaking memory TALLOC_CTX *frame = talloc_stackframe(); smbc_getFunctionPurgeCachedServers(smbContext)(smbContext); TALLOC_FREE(frame); smbc_free_context(smbContext, NO); } } + (id) fetchTreeAtPath:(NSString *)path auth:(KxSMBAuth *)auth { NSParameterAssert(path); SMBCCTX *smbContext = [self openSmbContext:auth]; if (!smbContext) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } id result = nil; SMBCFILE *smbFile = smbc_getFunctionOpendir(smbContext)(smbContext, path.UTF8String); if (smbFile) { NSMutableArray *ma = [NSMutableArray array]; struct smbc_dirent *dirent; smbc_readdir_fn readdirFn = smbc_getFunctionReaddir(smbContext); while((dirent = readdirFn(smbContext, smbFile)) != NULL) { if (!dirent->name) continue; if (!strlen(dirent->name)) continue; if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..") || !strcmp(dirent->name, "IPC$")) continue; NSString *name = [NSString stringWithUTF8String:dirent->name]; NSString *itemPath; if ([path characterAtIndex:path.length-1] == '/') itemPath = [path stringByAppendingString:name] ; else itemPath = [NSString stringWithFormat:@"%@/%@", path, name]; KxSMBItemStat *stat = nil; if (dirent->smbc_type != SMBC_WORKGROUP && dirent->smbc_type != SMBC_SERVER) { id r = [self fetchStat:smbContext atPath:itemPath]; if ([r isKindOfClass:[KxSMBItemStat class]]) { stat = r; } } switch(dirent->smbc_type) { case SMBC_WORKGROUP: case SMBC_SERVER: { KxSMBItem *item = [[KxSMBItemTree alloc] initWithType:dirent->smbc_type path:[NSString stringWithFormat:@"smb://%@", name] stat:nil auth:auth]; [ma addObject:item]; break; } case SMBC_FILE_SHARE: case SMBC_IPC_SHARE: case SMBC_DIR: { KxSMBItem *item = [[KxSMBItemTree alloc] initWithType:dirent->smbc_type path:itemPath stat:stat auth:auth]; [ma addObject:item]; break; } case SMBC_FILE: { KxSMBItem *item = [[KxSMBItemFile alloc] initWithType:KxSMBItemTypeFile path:itemPath stat:stat auth:auth]; [ma addObject:item]; break; } case SMBC_PRINTER_SHARE: case SMBC_COMMS_SHARE: case SMBC_LINK: { KxSMBItem *item = [[KxSMBItem alloc] initWithType:dirent->smbc_type path:itemPath stat:stat auth:auth]; [ma addObject:item]; break; } } } smbc_getFunctionClose(smbContext)(smbContext, smbFile); result = [ma copy]; } else { const int err = errno; result = mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable open dir:%@ (errno:%d)", nil), path, err); } [self closeSmbContext:smbContext]; return result; } + (id) fetchStat:(SMBCCTX *)smbContext atPath:(NSString *)path { NSParameterAssert(smbContext); NSParameterAssert(path); struct stat st; int r = smbc_getFunctionStat(smbContext)(smbContext, path.UTF8String, &st); if (r < 0) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable get stat:%@ (errno:%d)", nil), path, err); } KxSMBItemStat *stat = [[KxSMBItemStat alloc] init]; stat.lastModified = [NSDate dateWithTimeIntervalSince1970: st.st_mtime]; stat.lastAccess = [NSDate dateWithTimeIntervalSince1970: st.st_atime]; stat.creationTime = [NSDate dateWithTimeIntervalSince1970: st.st_ctime]; stat.size = st.st_size; stat.mode = st.st_mode; return stat; } + (id) fetchAtPath:(NSString *)path expandDir:(BOOL)expandDir auth:(KxSMBAuth *)auth { NSParameterAssert(path); if (![path hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), path); } NSString *sPath = [path substringFromIndex:@"smb://".length]; if (!sPath.length) { return [self fetchTreeAtPath:path auth:auth]; } if ([sPath hasSuffix:@"/"]) { sPath = [sPath substringToIndex:sPath.length - 1]; } if (sPath.pathComponents.count == 1) { // smb:// or smb://server/ or smb://workgroup/ return [self fetchTreeAtPath:path auth:auth]; } id result = nil; SMBCCTX *smbContext = [self openSmbContext:auth]; if (!smbContext) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } result = [self fetchStat:smbContext atPath:path]; if ([result isKindOfClass:[KxSMBItemStat class]]) { KxSMBItemStat *stat = result; if (S_ISDIR(stat.mode)) { if (expandDir) { result = [self fetchTreeAtPath:path auth:auth]; } else { result = [[KxSMBItemTree alloc] initWithType:KxSMBItemTypeDir path:path stat:stat auth:auth]; } } else if (S_ISREG(stat.mode)) { result = [[KxSMBItemFile alloc] initWithType:KxSMBItemTypeFile path:path stat:stat auth:auth]; } else { result = [[KxSMBItem alloc] initWithType:S_ISLNK(stat.mode) ? KxSMBItemTypeLink : KxSMBItemTypeUnknown path:path stat:stat auth:auth]; } } [self closeSmbContext:smbContext]; return result; } + (id) removeAtPath:(NSString *)path auth:(KxSMBAuth *)auth { NSParameterAssert(path); if (![path hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), path); } SMBCCTX *smbContext = [self openSmbContext:auth]; if (!smbContext) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } id result; int r = smbc_getFunctionUnlink(smbContext)(smbContext, path.UTF8String); if (r < 0) { int err = errno; if (err == EISDIR || err == EINVAL) { r = smbc_getFunctionRmdir(smbContext)(smbContext, path.UTF8String); if (r < 0) { err = errno; result = mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable rmdir file:%@ (errno:%d)", nil), path, err); } } else { result = mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable unlink file:%@ (errno:%d)", nil), path, err); } } [self closeSmbContext:smbContext]; return result; } + (id) createFolderAtPath:(NSString *)path auth:(KxSMBAuth *)auth { NSParameterAssert(path); if (![path hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), path); } SMBCCTX *smbContext = [self openSmbContext:auth]; if (!smbContext) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } id result; int r = smbc_getFunctionMkdir(smbContext)(smbContext, path.UTF8String, 0); if (r < 0) { const int err = errno; result = mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable mkdir:%@ (errno:%d)", nil), path, err); } else { id stat = [self fetchStat:smbContext atPath: path]; if ([stat isKindOfClass:[KxSMBItemStat class]]) { result = [[KxSMBItemTree alloc] initWithType:KxSMBItemTypeDir path:path stat:stat auth:auth]; } else { result = stat; } } [self closeSmbContext:smbContext]; return result; } + (id) createFileAtPath:(NSString *)path overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth { NSParameterAssert(path); if (![path hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), path); } KxSMBItemFile *itemFile = [[KxSMBItemFile alloc] initWithType:KxSMBItemTypeFile path:path stat:nil auth:auth]; id result = [itemFile createFile:overwrite]; if ([result isKindOfClass:[NSError class]]) { return result; } return itemFile; } + (NSError *) ensureLocalFolderExists:(NSString *)folderPath { NSFileManager *fm = [[NSFileManager alloc] init]; BOOL isDir; if ([fm fileExistsAtPath:folderPath isDirectory:&isDir]) { if (!isDir) { return mkKxSMBError(KxSMBErrorFileIO, NSLocalizedString(@"Cannot overwrite file %@", nil), folderPath); } } else { NSError *error; if (![fm createDirectoryAtPath:folderPath withIntermediateDirectories:NO attributes:nil error:&error]) { return error; } } return nil; } + (NSFileHandle *) createLocalFile:(NSString *)path overwrite:(BOOL) overwrite error:(NSError **)outError { NSFileManager *fm = [[NSFileManager alloc] init]; if ([fm fileExistsAtPath:path]) { if (overwrite) { if (![fm removeItemAtPath:path error:outError]) { return nil; } } else { return nil; } } NSString *folder = path.stringByDeletingLastPathComponent; if (![fm fileExistsAtPath:folder] && ![fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:outError]) { return nil; } if (![fm createFileAtPath:path contents:nil attributes:nil]) { if (outError) { *outError = mkKxSMBError(KxSMBErrorFileIO, NSLocalizedString(@"Unable create file", nil), path.lastPathComponent); } return nil; } return [NSFileHandle fileHandleForWritingToURL:[NSURL fileURLWithPath:path] error:outError]; } + (void) readSMBFile:(KxSMBItemFile *)smbFile fileHandle:(NSFileHandle *)fileHandle progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [smbFile readDataOfLength:1024*1024 block:^(id result) { if ([result isKindOfClass:[NSData class]]) { NSData *data = result; if (data.length) { [fileHandle writeData:data]; if (progress) { BOOL stop = NO; progress(smbFile, fileHandle.offsetInFile, &stop); if (stop) { // remove the local file from a disk NSString *filePath; char buffer[PATH_MAX] = {0}; if (fcntl(fileHandle.fileDescriptor, F_GETPATH, buffer) != -1) { filePath = [[NSString alloc] initWithUTF8String:buffer]; } //[fileHandle truncateFileAtOffset:0]; [fileHandle closeFile]; if (filePath.length) { [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; } block(nil); return; } } [self readSMBFile:smbFile fileHandle:fileHandle progress:progress block:block]; } else { [fileHandle closeFile]; block(@(YES)); // complete } return; } [fileHandle closeFile]; block([result isKindOfClass:[NSError class]] ? result : nil); }]; } + (void) copySMBFile:(KxSMBItemFile *)smbFile localPath:(NSString *)localPath overwrite:(BOOL)overwrite progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { NSError *error = nil; NSFileHandle *fileHandle = [self createLocalFile:localPath overwrite:overwrite error:&error]; if (fileHandle) { [self readSMBFile:smbFile fileHandle:fileHandle progress:progress block:block]; } else { if (!error) { error = mkKxSMBError(KxSMBErrorFileIO, NSLocalizedString(@"Cannot overwrite file %@", nil), localPath.lastPathComponent); } block(error); } } + (void) enumerateSMBFolders:(NSArray *)folders items:(NSMutableArray *)items block:(KxSMBBlock)block { KxSMBItemTree *folder = folders[0]; NSMutableArray *mfolders = [folders mutableCopy]; [mfolders removeObjectAtIndex:0]; [folder fetchItems:^(id result) { if ([result isKindOfClass:[NSArray class]]) { for (KxSMBItem *item in result ) { if ([item isKindOfClass:[KxSMBItemFile class]]) { [items addObject:item]; } else if ([item isKindOfClass:[KxSMBItemTree class]] && (item.type == KxSMBItemTypeDir || item.type == KxSMBItemTypeFileShare || item.type == KxSMBItemTypeServer)) { [mfolders addObject:item]; [items addObject:item]; } } if (mfolders.count) { [self enumerateSMBFolders:mfolders items:items block:block]; } else { block(items); } } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } + (void) copySMBItems:(NSArray *)smbItems smbFolder:(NSString *)smbFolder localFolder:(NSString *)localFolder overwrite:(BOOL)overwrite progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { KxSMBItem *item = smbItems[0]; if (smbItems.count > 1) { smbItems = [smbItems subarrayWithRange:NSMakeRange(1, smbItems.count - 1)]; } else { smbItems = nil; } if ([item isKindOfClass:[KxSMBItemFile class]]) { NSString *destPath = localFolder; NSString *itemFolder = item.path.stringByDeletingLastPathComponent; if (itemFolder.length > smbFolder.length) { NSString *relPath = [itemFolder substringFromIndex:smbFolder.length]; destPath = [destPath stringByAppendingPathComponent:relPath]; } destPath = [destPath stringByAppendingSMBPathComponent:item.path.lastPathComponent]; [self copySMBFile:(KxSMBItemFile *)item localPath:destPath overwrite:overwrite progress:progress block:^(id result) { if ([result isKindOfClass:[NSError class]]) { block(result); } else { if (smbItems.count) { [self copySMBItems:smbItems smbFolder:smbFolder localFolder:localFolder overwrite:overwrite progress:progress block:block]; } else { block(@(YES)); // complete } } }]; } else if ([item isKindOfClass:[KxSMBItemTree class]]) { NSString *destPath = localFolder; NSString *itemFolder = item.path; if (itemFolder.length > smbFolder.length) { NSString *relPath = [itemFolder substringFromIndex:smbFolder.length]; destPath = [destPath stringByAppendingPathComponent:relPath]; } NSError *error = [self ensureLocalFolderExists:destPath]; if (error) { block(error); return; } if (smbItems.count) { [self copySMBItems:smbItems smbFolder:smbFolder localFolder:localFolder overwrite:overwrite progress:progress block:block]; } else { block(@(YES)); // complete } } } /// + (void) writeSMBFile:(KxSMBItemFile *)smbFile fileHandle:(NSFileHandle *)fileHandle progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { NSData *data; @try { data = [fileHandle readDataOfLength:1024*1024]; } @catch (NSException *exception) { [fileHandle closeFile]; block(mkKxSMBError(KxSMBErrorFileIO, [exception description])); return; } if (data.length) { [smbFile writeData:data block:^(id result) { if ([result isKindOfClass:[NSNumber class]]) { if (progress) { BOOL stop = NO; progress(smbFile, fileHandle.offsetInFile, &stop); if (stop) { [fileHandle closeFile]; // remove the smbfile from a share NSString *smbPath = smbFile.path; [smbFile close]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ [KxSMBProvider removeAtPath:smbPath auth:smbFile.auth]; }]; block(nil); return; } } [self writeSMBFile:smbFile fileHandle:fileHandle progress:progress block:block]; return; } block([result isKindOfClass:[NSError class]] ? result : nil); }]; } else { [fileHandle closeFile]; block(smbFile); } } + (void) copyLocalFile:(NSString *)localPath smbPath:(NSString *)smbPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider createFileAtPath:smbPath overwrite:overwrite auth:auth block:^(id result) { if ([result isKindOfClass:[KxSMBItemFile class]]) { NSError *error = nil; NSFileHandle *fileHandle; NSURL *url =[NSURL fileURLWithPath:localPath]; fileHandle = [NSFileHandle fileHandleForReadingFromURL:url error:&error]; if (fileHandle) { [self writeSMBFile:result fileHandle:fileHandle progress:progress block:block]; } else { block(error); } } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } + (void) copyLocalFiles:(NSDirectoryEnumerator *)enumerator localFolder:(NSString *)localFolder smbFolder:(KxSMBItemTree *)smbFolder overwrite:(BOOL)overwrite progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { NSString *path = [enumerator nextObject]; if (path) { if (path.length && [path characterAtIndex:0] != '.') { NSDictionary *attr = [enumerator fileAttributes]; if ([[attr fileType] isEqualToString:NSFileTypeDirectory]) { KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider createFolderAtPath:[smbFolder.path stringByAppendingSMBPathComponent:path] auth:smbFolder.auth block:^(id result) { if ([result isKindOfClass:[NSError class]]) { block(result); } else { [self copyLocalFiles:enumerator localFolder:localFolder smbFolder:smbFolder overwrite:overwrite progress:progress block:block]; } }]; return; } else if ([[attr fileType] isEqualToString:NSFileTypeRegular]) { NSString *destFolder = smbFolder.path; NSString *fileFolder = path.stringByDeletingLastPathComponent; if (fileFolder.length) { destFolder = [destFolder stringByAppendingSMBPathComponent:fileFolder]; } [self copyLocalFile:[localFolder stringByAppendingPathComponent:path] smbPath:[destFolder stringByAppendingSMBPathComponent:path.lastPathComponent] overwrite:overwrite auth:smbFolder.auth progress:progress block:^(id result) { if ([result isKindOfClass:[NSError class]]) { block(result); } else { [self copyLocalFiles:enumerator localFolder:localFolder smbFolder:smbFolder overwrite:overwrite progress:progress block:block]; } }]; return; } } [self copyLocalFiles:enumerator localFolder:localFolder smbFolder:smbFolder overwrite:overwrite progress:progress block:block]; } else { block(smbFolder); } } + (void) copyFileFromPath:(NSString *)oldPath toPath:(NSString *)newPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider fetchAtPath:oldPath expandDir:NO auth:auth block:^(id result) { if ([result isKindOfClass:[KxSMBItemFile class]]) { KxSMBItemFile *fromFile = result; [provider createFileAtPath:newPath overwrite:overwrite auth:auth block:^(id result) { if ([result isKindOfClass:[KxSMBItemFile class]]) { KxSMBItemFile *toFile = result; [self copyFromSMBFile:fromFile toSMBFile:toFile progress:progress block:block]; } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } else if ([result isKindOfClass:[KxSMBItemTree class]]) { block(mkKxSMBError(KxSMBErrorPathIsDir, newPath, 0)); } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } + (void) copyFromSMBFile:(KxSMBItemFile *)fromFile toSMBFile:(KxSMBItemFile *)toFile progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [fromFile readDataOfLength:1024*1024 block:^(id result) { if ([result isKindOfClass:[NSData class]]) { NSData *data = result; if (data.length) { [toFile writeData:data block:^(id result) { if ([result isKindOfClass:[NSNumber class]]) { if (progress) { BOOL stop = NO; progress(fromFile, 0, &stop); if (stop) { // remove the dest smbfile from a share NSString *smbPath = toFile.path; [toFile close]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ [KxSMBProvider removeAtPath:smbPath auth:toFile.auth]; }]; block(nil); return; } } [self copyFromSMBFile:fromFile toSMBFile:toFile progress:progress block:block]; return; } block([result isKindOfClass:[NSError class]] ? result : nil); }]; } else { block(toFile); // complete } return; } block([result isKindOfClass:[NSError class]] ? result : nil); }]; } /// + (void) removeSMBItems:(NSArray *)smbItems block:(KxSMBBlock)block { KxSMBItem *item = smbItems[0]; if (smbItems.count > 1) { smbItems = [smbItems subarrayWithRange:NSMakeRange(1, smbItems.count - 1)]; } else { smbItems = nil; } KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider removeAtPath:item.path auth:item.auth block:^(id result) { if ([result isKindOfClass:[NSError class]]) { block(result); } else if (smbItems.count) { [self removeSMBItems:smbItems block:block]; } else { block(@(YES)); } }]; } + (id) renameAtPath:(NSString *)oldPath newPath:(NSString *)newPath auth:(KxSMBAuth *)auth { NSParameterAssert(oldPath); NSParameterAssert(newPath); if (![oldPath hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), oldPath); } if (![newPath hasPrefix:@"smb://"]) { return mkKxSMBError(KxSMBErrorInvalidProtocol, NSLocalizedString(@"Path:%@", nil), newPath); } SMBCCTX *smbContext = [self openSmbContext:auth]; if (!smbContext) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } id result; int r = smbc_getFunctionRename(smbContext)(smbContext, oldPath.UTF8String, smbContext, newPath.UTF8String); if (r < 0) { const int err = errno; result = mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable rename file:%@ (errno:%d)", nil), oldPath, err); } else { result = [self fetchStat:smbContext atPath: newPath]; if ([result isKindOfClass:[KxSMBItemStat class]]) { KxSMBItemStat *stat = result; if (S_ISDIR(stat.mode)) { result = [[KxSMBItemTree alloc] initWithType:KxSMBItemTypeDir path:newPath stat:stat auth:auth]; } else if (S_ISREG(stat.mode)) { result = [[KxSMBItemFile alloc] initWithType:KxSMBItemTypeFile path:newPath stat:stat auth:auth]; } else { result = nil; } } } [self closeSmbContext:smbContext]; return result; } + (void) fireBlock:(KxSMBBlock)block withResult:(id)result { dispatch_queue_t queue = [KxSMBProvider sharedSmbProvider].completionQueue; if (queue) { dispatch_async(queue, ^{ block(result); }); } else { block(result); } } #pragma mark - internal methods - (void) dispatchSync: (dispatch_block_t) block { dispatch_sync(_dispatchQueue, block); } - (void) dispatchAsync: (dispatch_block_t) block { dispatch_async(_dispatchQueue, block); } #pragma mark - public methods - (void) fetchAtPath:(NSString *)path expandDir:(BOOL)expandDir auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { NSParameterAssert(path); NSParameterAssert(block); dispatch_async(_dispatchQueue, ^{ id result = [KxSMBProvider fetchAtPath:(path.length ? path : @"smb://") expandDir:expandDir auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }); } - (void) fetchAtPath:(NSString *)path auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { [self fetchAtPath:path expandDir:YES auth:auth block:block]; } - (id) fetchAtPath:(NSString *)path expandDir:(BOOL)expandDir auth:(KxSMBAuth *)auth { NSParameterAssert(path); __block id result = nil; dispatch_sync(_dispatchQueue, ^{ result = [KxSMBProvider fetchAtPath:(path.length ? path : @"smb://") expandDir:expandDir auth:auth]; }); return result; } - (id) fetchAtPath:(NSString *)path auth:(KxSMBAuth *)auth { return [self fetchAtPath:path expandDir:YES auth:auth]; } - (void) createFileAtPath:(NSString *)path overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth block:(KxSMBBlock) block { NSParameterAssert(path); NSParameterAssert(block); dispatch_async(_dispatchQueue, ^{ id result = [KxSMBProvider createFileAtPath:path overwrite:overwrite auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }); } - (id) createFileAtPath:(NSString *)path overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth { NSParameterAssert(path); __block id result = nil; dispatch_sync(_dispatchQueue, ^{ result = [KxSMBProvider createFileAtPath:path overwrite:overwrite auth:auth]; }); return result; } - (void) removeAtPath:(NSString *)path auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { NSParameterAssert(path); NSParameterAssert(block); dispatch_async(_dispatchQueue, ^{ id result = [KxSMBProvider removeAtPath:path auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }); } - (id) removeAtPath:(NSString *)path auth:(KxSMBAuth *)auth { NSParameterAssert(path); __block id result = nil; dispatch_sync(_dispatchQueue, ^{ result = [KxSMBProvider removeAtPath:path auth:auth]; }); return result; } - (void) createFolderAtPath:(NSString *)path auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { NSParameterAssert(path); NSParameterAssert(block); dispatch_async(_dispatchQueue, ^{ id result = [KxSMBProvider createFolderAtPath:path auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }); } - (id) createFolderAtPath:(NSString *)path auth:(KxSMBAuth *)auth { NSParameterAssert(path); __block id result = nil; dispatch_sync(_dispatchQueue, ^{ result = [KxSMBProvider createFolderAtPath:path auth:auth]; }); return result; } - (void) copySMBPath:(NSString *)smbPath localPath:(NSString *)localPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { [self copySMBPath:smbPath localPath:localPath overwrite:overwrite auth:auth progress:nil block:block];; } - (void) copyLocalPath:(NSString *)localPath smbPath:(NSString *)smbPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { [self copyLocalPath:localPath smbPath:smbPath overwrite:overwrite auth:auth progress:nil block:block]; } - (void) copySMBPath:(NSString *)smbPath localPath:(NSString *)localPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [self fetchAtPath:smbPath expandDir:YES auth:auth block:^(id result) { if ([result isKindOfClass:[KxSMBItemFile class]]) { [KxSMBProvider copySMBFile:result localPath:localPath overwrite:overwrite progress:progress block:block]; } else if ([result isKindOfClass:[NSArray class]]) { NSError *error = [KxSMBProvider ensureLocalFolderExists:localPath]; if (error) { block(error); return; } NSMutableArray *folders = [NSMutableArray array]; NSMutableArray *items = [NSMutableArray array]; for (KxSMBItem *item in result ) { if ([item isKindOfClass:[KxSMBItemFile class]]) { [items addObject:item]; } else if ([item isKindOfClass:[KxSMBItemTree class]] && (item.type == KxSMBItemTypeDir || item.type == KxSMBItemTypeFileShare || item.type == KxSMBItemTypeServer)) { [items addObject:item]; [folders addObject:item]; } } if (folders.count) { [KxSMBProvider enumerateSMBFolders:folders items:items block:^(id result) { if ([result isKindOfClass:[NSArray class]]) { NSArray *items = result; if (items.count) { [KxSMBProvider copySMBItems:items smbFolder:smbPath localFolder:localPath overwrite:overwrite progress:progress block:block]; } else { block(@(YES)); } } else { block(result); } }]; } else if (items.count) { [KxSMBProvider copySMBItems:items smbFolder:smbPath localFolder:localPath overwrite:overwrite progress:progress block:block]; } else { block(@(YES)); return; } } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } - (void) copyLocalPath:(NSString *)localPath smbPath:(NSString *)smbPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { NSFileManager *fm = [[NSFileManager alloc] init]; BOOL isDir; if (![fm fileExistsAtPath:localPath isDirectory:&isDir]) { block(mkKxSMBError(KxSMBErrorFileIO, NSLocalizedString(@"File '%@' is not exist", nil), localPath.lastPathComponent)); return; } if (isDir) { [self createFolderAtPath:smbPath auth:auth block:^(id result) { if ([result isKindOfClass:[KxSMBItemTree class]]) { [KxSMBProvider copyLocalFiles:[fm enumeratorAtPath:localPath] localFolder:localPath smbFolder:result overwrite:overwrite progress:progress block:block]; } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } else { [KxSMBProvider copyLocalFile:localPath smbPath:smbPath overwrite:overwrite auth:auth progress:progress block:block]; } } - (void) copyFromPath:(NSString *)oldPath toPath:(NSString *)newPath overwrite:(BOOL)overwrite auth:(KxSMBAuth *)auth progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [KxSMBProvider copyFileFromPath:oldPath toPath:newPath overwrite:overwrite auth:auth progress:progress block:block]; } - (void) removeFolderAtPath:(NSString *)path auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { [self fetchAtPath:path expandDir:YES auth:auth block:^(id result) { if ([result isKindOfClass:[NSArray class]]) { NSMutableArray *folders = [NSMutableArray array]; NSMutableArray *items = [NSMutableArray array]; for (KxSMBItem *item in result) { if ([item isKindOfClass:[KxSMBItemFile class]]) { [items addObject:item]; } else if ([item isKindOfClass:[KxSMBItemTree class]] && (item.type == KxSMBItemTypeDir || item.type == KxSMBItemTypeFileShare || item.type == KxSMBItemTypeServer)) { [items addObject:item]; [folders addObject:item]; } } if (folders.count) { [KxSMBProvider enumerateSMBFolders:folders items:items block:^(id result) { if ([result isKindOfClass:[NSArray class]]) { NSMutableArray *reversed = [NSMutableArray array]; for (id item in [result reverseObjectEnumerator]) { [reversed addObject:item]; } [KxSMBProvider removeSMBItems:reversed block:^(id result) { if ([result isKindOfClass:[NSNumber class]]) { [[KxSMBProvider sharedSmbProvider] removeAtPath:path auth:auth block:block]; } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } else if (items.count) { [KxSMBProvider removeSMBItems:items block:^(id result) { if ([result isKindOfClass:[NSNumber class]]) { [[KxSMBProvider sharedSmbProvider] removeAtPath:path auth:auth block:block]; } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } else { [self removeAtPath:path auth:auth block:block]; } } else if ([result isKindOfClass:[KxSMBItemFile class]]) { block(mkKxSMBError(KxSMBErrorPathIsNotDir, path)); } else { block([result isKindOfClass:[NSError class]] ? result : nil); } }]; } - (void) renameAtPath:(NSString *)oldPath newPath:(NSString *)newPath auth:(KxSMBAuth *)auth block:(KxSMBBlock)block { NSParameterAssert(oldPath); NSParameterAssert(newPath); NSParameterAssert(block); dispatch_async(_dispatchQueue, ^{ id result = [KxSMBProvider renameAtPath:oldPath newPath:newPath auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }); } #pragma mark - compatible (without auth) // without auth (compatible) - (void) fetchAtPath:(NSString *)path block:(KxSMBBlock)block { [self fetchAtPath:path expandDir:YES auth:nil block:block]; } - (id) fetchAtPath:(NSString *)path { return [self fetchAtPath:path expandDir:YES auth:nil]; } - (void) createFileAtPath:(NSString *)path overwrite:(BOOL)overwrite block:(KxSMBBlock)block { [self createFileAtPath:path overwrite:overwrite auth:nil block:block]; } - (id) createFileAtPath:(NSString *)path overwrite:(BOOL)overwrite { return [self createFileAtPath:path overwrite:overwrite auth:nil]; } - (void) createFolderAtPath:(NSString *)path block:(KxSMBBlock)block { [self createFolderAtPath:path auth:nil block:block]; } - (id) createFolderAtPath:(NSString *)path { return [self createFolderAtPath:path auth:nil]; } - (void) removeAtPath:(NSString *)path block:(KxSMBBlock)block { [self removeAtPath:path auth:nil block:block]; } - (id) removeAtPath:(NSString *)path { return [self removeAtPath:path auth:nil]; } - (void) copySMBPath:(NSString *)smbPath localPath:(NSString *)localPath overwrite:(BOOL)overwrite block:(KxSMBBlock)block { [self copySMBPath:smbPath localPath:localPath overwrite:overwrite auth:nil block:block]; } - (void) copyLocalPath:(NSString *)localPath smbPath:(NSString *)smbPath overwrite:(BOOL)overwrite block:(KxSMBBlock)block { [self copyLocalPath:localPath smbPath:smbPath overwrite:overwrite auth:nil block:block]; } - (void) copySMBPath:(NSString *)smbPath localPath:(NSString *)localPath overwrite:(BOOL)overwrite progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [self copySMBPath:smbPath localPath:localPath overwrite:overwrite auth:nil progress:progress block:block]; } - (void) copyLocalPath:(NSString *)localPath smbPath:(NSString *)smbPath overwrite:(BOOL)overwrite progress:(KxSMBBlockProgress)progress block:(KxSMBBlock)block { [self copyLocalPath:localPath smbPath:smbPath overwrite:overwrite auth:nil progress:progress block:block]; } - (void) removeFolderAtPath:(NSString *)path block:(KxSMBBlock)block { [self removeFolderAtPath:path auth:nil block:block]; } - (void) renameAtPath:(NSString *)oldPath newPath:(NSString *)newPath block:(KxSMBBlock)block { [self renameAtPath:oldPath newPath:newPath auth:nil block:block]; } #if 0 + (void) dumpSmbcOptions:(SMBCCTX *)smbContext { NSLog(@"Debug: %d", smbc_getDebug(smbContext)); NSLog(@"NetbiosName: %s", smbc_getNetbiosName(smbContext)); NSLog(@"Workgroup: %s", smbc_getWorkgroup(smbContext)); NSLog(@"User: %s", smbc_getUser(smbContext)); NSLog(@"Timeout: %d", smbc_getTimeout(smbContext)); NSLog(@"DebugToStderr: %d", smbc_getOptionDebugToStderr(smbContext)); NSLog(@"FullTimeNames: %d", smbc_getOptionFullTimeNames(smbContext)); NSLog(@"OpenShareMode: %d", smbc_getOptionOpenShareMode(smbContext)); NSLog(@"EncryptionLevel: %d", smbc_getOptionSmbEncryptionLevel(smbContext)); NSLog(@"CaseSensitive: %d", smbc_getOptionCaseSensitive(smbContext)); NSLog(@"BrowseMaxLmbCount: %d", smbc_getOptionBrowseMaxLmbCount(smbContext)); NSLog(@"UrlEncodeReaddirEntries: %d", smbc_getOptionUrlEncodeReaddirEntries(smbContext)); NSLog(@"OneSharePerServer: %d", smbc_getOptionOneSharePerServer(smbContext)); NSLog(@"UseKerberos: %d", smbc_getOptionUseKerberos(smbContext)); NSLog(@"FallbackAfterKerberos: %d", smbc_getOptionFallbackAfterKerberos(smbContext)); NSLog(@"NoAutoAnonymousLogin: %d", smbc_getOptionNoAutoAnonymousLogin(smbContext)); NSLog(@"UseCCache: %d", smbc_getOptionUseCCache(smbContext)); NSLog(@"UseNTHash: %d", smbc_getOptionUseNTHash(smbContext)); } #endif @end /////////////////////////////////////////////////////////////////////////////// @implementation KxSMBItemTree - (void) fetchItems:(KxSMBBlock) block { NSParameterAssert(block); NSString *path = self.path; KxSMBAuth *auth = self.auth; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync: ^{ id result = [KxSMBProvider fetchTreeAtPath:path auth:auth]; [KxSMBProvider fireBlock:block withResult:result]; }]; } - (id) fetchItems { __block id result = nil; NSString *path = self.path; KxSMBAuth *auth = self.auth; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchSync: ^{ result = [KxSMBProvider fetchTreeAtPath:path auth:auth]; }]; return result; } - (void) createFileWithName:(NSString *)name overwrite:(BOOL)overwrite block:(KxSMBBlock)block { NSParameterAssert(name.length); if (self.type != KxSMBItemTypeDir || self.type != KxSMBItemTypeFileShare) { block(mkKxSMBError(KxSMBErrorPathIsNotDir, nil)); return; } [[KxSMBProvider sharedSmbProvider] createFileAtPath:[self.path stringByAppendingSMBPathComponent:name] overwrite:overwrite auth:self.auth block:block]; } - (id) createFileWithName:(NSString *)name overwrite:(BOOL)overwrite { NSParameterAssert(name.length); if (self.type != KxSMBItemTypeDir || self.type != KxSMBItemTypeFileShare ) { return mkKxSMBError(KxSMBErrorPathIsNotDir, nil); } NSString *path = [self.path stringByAppendingSMBPathComponent:name]; return [[KxSMBProvider sharedSmbProvider] createFileAtPath:path overwrite:overwrite auth:self.auth]; } - (void) removeWithName:(NSString *)name block:(KxSMBBlock)block { if (self.type != KxSMBItemTypeDir || self.type != KxSMBItemTypeFileShare) { block(mkKxSMBError(KxSMBErrorPathIsNotDir, nil)); return; } NSString *path = [self.path stringByAppendingSMBPathComponent:name]; [[KxSMBProvider sharedSmbProvider] removeAtPath:path auth:self.auth block:block]; } - (id) removeWithName:(NSString *)name { if (self.type != KxSMBItemTypeDir || self.type != KxSMBItemTypeFileShare ) { return mkKxSMBError(KxSMBErrorPathIsNotDir, nil); } NSString *path = [self.path stringByAppendingSMBPathComponent:name]; return [[KxSMBProvider sharedSmbProvider] removeAtPath:path auth:self.auth]; } @end /////////////////////////////////////////////////////////////////////////////// @interface KxSMBFileImpl : NSObject @end @implementation KxSMBFileImpl { SMBCCTX *_context; SMBCFILE *_file; NSString *_path; KxSMBAuth *_auth; } - (id) initWithPath:(NSString *)path auth:(KxSMBAuth *)auth { self = [super init]; if (self) { _path = path; _auth = auth; } return self; } - (NSError *) openFile { _context = [KxSMBProvider openSmbContext:_auth]; if (!_context) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } _file = smbc_getFunctionOpen(_context)(_context, _path.UTF8String, O_RDONLY, 0); if (!_file) { [KxSMBProvider closeSmbContext:_context]; _context = NULL; const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable open file:%@ (errno:%d)", nil), _path, err); } return nil; } - (NSError *) createFile:(BOOL)overwrite { _context = [KxSMBProvider openSmbContext:_auth]; if (!_context) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable init SMB context (errno:%d)", nil), err); } _file = smbc_getFunctionCreat(_context)(_context, _path.UTF8String, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL)); if (!_file) { [KxSMBProvider closeSmbContext:_context]; _context = NULL; const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable open file:%@ (errno:%d)", nil), _path, err); } return nil; } - (void) closeFile { if (_file) { smbc_getFunctionClose(_context)(_context, _file); _file = NULL; } if (_context) { [KxSMBProvider closeSmbContext:_context]; _context = NULL; } } - (id)readDataOfLength:(NSUInteger)length { if (!_file) { NSError *error = [self openFile]; if (error) return error; } // it seems 512 kb is an optimal buffer size const size_t bufferSize = MIN(length, 512*1024); Byte *buffer = malloc(bufferSize); if (!buffer) { return mkKxSMBError(KxSMBErrorOutOfMemory, nil); } smbc_read_fn readFn = smbc_getFunctionRead(_context); NSMutableData *md = [NSMutableData data]; NSInteger bytesToRead = length; while (bytesToRead > 0) { ssize_t r = readFn(_context, _file, buffer, MIN(bytesToRead, bufferSize)); if (r == 0) break; if (r < 0) { const int err = errno; free(buffer); return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable read file:%@ (errno:%d)", nil), _path, err); } [md appendBytes:buffer length:r]; bytesToRead -= r; } free(buffer); return md; } - (id)readDataToEndOfFile { if (!_file) { NSError *error = [self openFile]; if (error) return error; } Byte buffer[32768]; smbc_read_fn readFn = smbc_getFunctionRead(_context); NSMutableData *md = [NSMutableData data]; while (1) { ssize_t r = readFn(_context, _file, buffer, sizeof(buffer)); if (r == 0) break; if (r < 0) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable read file:%@ (errno:%d)", nil), _path, err); } [md appendBytes:buffer length:r]; } return md; } - (id)seekToFileOffset:(off_t)offset whence:(NSInteger)whence { if (!_file) { NSError *error = [self openFile]; if (error) return error; } off_t r = smbc_getFunctionLseek(_context)(_context, _file, offset, (int)whence); if (r < 0) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable seek to file:%@ (errno:%d)", nil), _path, errno); } return @(r); } - (id)writeData:(NSData *)data { if (!_file) { NSError *error = [self createFile:NO]; if (error) return error; } smbc_write_fn writeFn = smbc_getFunctionWrite(_context); NSInteger bytesToWrite = data.length; const Byte *bytes = data.bytes; while (bytesToWrite > 0) { ssize_t r = writeFn(_context, _file, bytes, bytesToWrite); if (r == 0) break; if (r < 0) { const int err = errno; return mkKxSMBError(errnoToSMBErr(err), NSLocalizedString(@"Unable write file:%@ (errno:%d)", nil), _path, err); } bytesToWrite -= r; bytes += r; } return @(data.length - bytesToWrite); } @end @implementation KxSMBItemFile { KxSMBFileImpl *_impl; } - (void) dealloc { [self close]; } - (void) close { if (_impl) { KxSMBFileImpl *p = _impl; _impl = nil; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ [p closeFile]; }]; } } - (KxSMBFileImpl *)theImpl { if (!_impl) { _impl = [[KxSMBFileImpl alloc] initWithPath:self.path auth:self.auth]; } return _impl; } - (void)readDataOfLength:(NSUInteger)length block:(KxSMBBlock)block { NSParameterAssert(block); KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ id result = [p readDataOfLength:length]; [KxSMBProvider fireBlock:block withResult:result]; }]; } - (id)readDataOfLength:(NSUInteger)length { __block id result = nil; KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchSync:^{ result = [p readDataOfLength:length]; }]; return result; } - (void)readDataToEndOfFile:(KxSMBBlock)block { NSParameterAssert(block); KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ id result = [p readDataToEndOfFile]; [KxSMBProvider fireBlock:block withResult:result]; }]; } - (id)readDataToEndOfFile { __block id result = nil; KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchSync:^{ result = [p readDataToEndOfFile]; }]; return result; } - (void)seekToFileOffset:(off_t)offset whence:(NSInteger)whence block:(KxSMBBlock) block { NSParameterAssert(block); KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ id result = [p seekToFileOffset:offset whence:whence]; [KxSMBProvider fireBlock:block withResult:result]; }]; } - (id)seekToFileOffset:(off_t)offset whence:(NSInteger)whence { __block id result = nil; KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchSync:^{ result = [p seekToFileOffset:offset whence:whence]; }]; return result; } - (void)writeData:(NSData *)data block:(KxSMBBlock) block { NSParameterAssert(block); KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchAsync:^{ id result = [p writeData:data]; [KxSMBProvider fireBlock:block withResult:result]; }]; } - (id)writeData:(NSData *)data { __block id result = nil; KxSMBFileImpl *p = self.theImpl; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider dispatchSync:^{ result = [p writeData:data]; }]; return result; } #pragma mark - internal - (id) createFile:(BOOL)overwrite { return [self.theImpl createFile:overwrite]; } @end /////////////////////////////////////////////////////////////////////////////// static void my_smbc_get_auth_data_fn(const char *srv, const char *shr, char *workgroup, int wglen, char *username, int unlen, char *password, int pwlen) { KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; KxSMBAuth *auth = nil; __strong id delegate = provider.delegate; if (delegate) { auth = [delegate smbRequestAuthServer:[NSString stringWithUTF8String:srv] share:[NSString stringWithUTF8String:shr] workgroup:[NSString stringWithUTF8String:workgroup] username:[NSString stringWithUTF8String:username]]; } if (username) { if (auth.username.length) { strncpy(username, auth.username.UTF8String, unlen - 1); } else { strncpy(username, "guest", unlen - 1); } } if (password) { if (auth.password.length) { strncpy(password, auth.password.UTF8String, pwlen - 1); } else { password[0] = 0; } } if (workgroup) { if (auth.workgroup.length) { strncpy(workgroup, auth.workgroup.UTF8String, wglen - 1); } else { workgroup[0] = 0; } } // NSLog(@"smb get auth for %s/%s -> %s/%s:%s", srv, shr, workgroup, username, password); } static void my_smbc_get_auth_data_with_context_fn(SMBCCTX *c, const char *srv, const char *shr, char *workgroup, int wglen, char *username, int unlen, char *password, int pwlen) { void *userdata = smbc_getOptionUserData(c); if (userdata) { KxSMBAuth *auth = (__bridge KxSMBAuth *)userdata; if (username) { if (auth.username.length) { strncpy(username, auth.username.UTF8String, unlen - 1); } else { strncpy(username, "guest", unlen - 1); } } if (password) { if (auth.password.length) { strncpy(password, auth.password.UTF8String, pwlen - 1); } else { password[0] = 0; } } if (workgroup) { if (auth.workgroup.length) { strncpy(workgroup, auth.workgroup.UTF8String, wglen - 1); } else { workgroup[0] = 0; } } } else { my_smbc_get_auth_data_fn(srv, shr, workgroup, wglen, userdata, unlen, password, pwlen); } } /////////////////////////////////////////////////////////////////////////////// @implementation NSString (KxSMB) // unfortunately, [NSString stringByAppendingPathComponent] brokes smb:// pathes // so need to use custom version - (NSString *) stringByAppendingSMBPathComponent: (NSString *) aString { NSString *path = self; if (![path hasSuffix:@"/"]) { path = [path stringByAppendingString:@"/"]; } return [path stringByAppendingString:aString]; } @end ================================================ FILE: KxSMBSample/AppDelegate.h ================================================ // // AppDelegate.h // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 27.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: KxSMBSample/AppDelegate.m ================================================ // // AppDelegate.m // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 27.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "AppDelegate.h" #import "TreeViewController.h" #import "SmbAuthViewController.h" #import "KxSMBProvider.h" @interface AppDelegate() @end @implementation AppDelegate { TreeViewController *_headVC; NSMutableDictionary *_cachedAuths; SmbAuthViewController *_smbAuthViewController; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _headVC = [[TreeViewController alloc] initAsHeadViewController]; //_headVC.defaultAuth = [KxSMBAuth smbAuthWorkgroup:@"" username:@"guest" password:@""]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:_headVC]; [self.window makeKeyAndVisible]; _cachedAuths = [NSMutableDictionary dictionary]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; provider.delegate = self; provider.config.browseMaxLmbCount = 0; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { } - (void)applicationDidEnterBackground:(UIApplication *)application { } - (void)applicationWillEnterForeground:(UIApplication *)application { } - (void)applicationDidBecomeActive:(UIApplication *)application { } - (void)applicationWillTerminate:(UIApplication *)application { } #pragma mark - KxSMBProviderDelegate - (void) presentSmbAuthViewControllerForServer:(NSString *)server share:(NSString *)share workgroup:(NSString *)workgroup username:(NSString *)username { if (!_smbAuthViewController) { _smbAuthViewController = [[SmbAuthViewController alloc] init]; _smbAuthViewController.delegate = self; _smbAuthViewController.username = @"guest"; } UINavigationController *nav = (UINavigationController *)self.window.rootViewController; if (nav.presentedViewController) return; _smbAuthViewController.server = server; _smbAuthViewController.workgroup = workgroup; _smbAuthViewController.username = username; UIViewController *vc = [[UINavigationController alloc] initWithRootViewController:_smbAuthViewController]; [nav.topViewController presentViewController:vc animated:NO completion:nil]; } - (void) couldSmbAuthViewController:(SmbAuthViewController *) controller done:(BOOL) done { if (done) { KxSMBAuth *auth = [KxSMBAuth smbAuthWorkgroup:controller.workgroup username:controller.username password:controller.password]; _cachedAuths[controller.server.uppercaseString] = auth; NSLog(@"store auth %@ -> (%@) %@:%@", controller.server, controller.workgroup, controller.username, controller.password); } UINavigationController *nav = (UINavigationController *)self.window.rootViewController; [nav dismissViewControllerAnimated:YES completion:nil]; [_headVC reloadPath]; } - (KxSMBAuth *) smbRequestAuthServer:(NSString *)server share:(NSString *)share workgroup:(NSString *)workgroup username:(NSString *)username { if ([share isEqualToString:@"IPC$"] || [share hasSuffix:@"$"]) { // return nil; } KxSMBAuth *auth = _cachedAuths[server.uppercaseString]; if (auth) { // NSLog(@"cached auth for %@ -> %@ (%@) %@:%@", server, share, auth.workgroup, auth.username, auth.password); return auth; } NSLog(@"ask auth for %@/%@ (%@)", server, share, workgroup); dispatch_async(dispatch_get_main_queue(), ^{ [self presentSmbAuthViewControllerForServer:server share:share workgroup:workgroup username:username]; }); return nil; } @end ================================================ FILE: KxSMBSample/FileViewController.h ================================================ // // FileViewController.h // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 29.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @class KxSMBItemFile; @interface FileViewController : UIViewController @property (readwrite, nonatomic, strong) KxSMBItemFile* smbFile; @end ================================================ FILE: KxSMBSample/FileViewController.m ================================================ // // FileViewController.m // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 29.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "FileViewController.h" #import "KxSMBProvider.h" #import @interface FileViewController () @end @implementation FileViewController { UIView *_container; UILabel *_nameLabel; UILabel *_sizeLabel; UILabel *_modifiedLabel; UILabel *_createdLabel; UIButton *_downloadButton; UIProgressView *_downloadProgress; UILabel *_downloadLabel; NSString *_filePath; NSFileHandle *_fileHandle; long _downloadedBytes; NSDate *_timestamp; } - (id)init { self = [super initWithNibName:nil bundle:nil]; if (self) { } return self; } - (void) dealloc { [self closeFiles]; } - (void)viewDidLoad { [super viewDidLoad]; const CGSize size = self.view.bounds.size; const CGFloat W = size.width; _container = [[UIView alloc] initWithFrame:(CGRect){0,0,size}]; _container.autoresizingMask = UIViewAutoresizingNone; _container.backgroundColor = [UIColor whiteColor]; [self.view addSubview:_container]; _nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, W - 20, 25)]; _nameLabel.font = [UIFont boldSystemFontOfSize:16]; _nameLabel.textColor = [UIColor darkTextColor]; _nameLabel.opaque = NO; _nameLabel.backgroundColor = [UIColor clearColor]; _nameLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; _sizeLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 35, W - 20, 25)]; _sizeLabel.font = [UIFont systemFontOfSize:14]; _sizeLabel.textColor = [UIColor darkTextColor]; _sizeLabel.opaque = NO; _sizeLabel.backgroundColor = [UIColor clearColor]; _sizeLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; _modifiedLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 60, W - 20, 25)]; _modifiedLabel.font = [UIFont systemFontOfSize:14];; _modifiedLabel.textColor = [UIColor darkTextColor]; _modifiedLabel.opaque = NO; _modifiedLabel.backgroundColor = [UIColor clearColor]; _modifiedLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; _createdLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 85, W - 20, 25)]; _createdLabel.font = [UIFont systemFontOfSize:14];; _createdLabel.textColor = [UIColor darkTextColor]; _createdLabel.opaque = NO; _createdLabel.backgroundColor = [UIColor clearColor]; _createdLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; _downloadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; _downloadButton.frame = CGRectMake(10, 120, 100, 30); _downloadButton.titleLabel.font = [UIFont boldSystemFontOfSize:16]; [_downloadButton setTitle:@"Download" forState:UIControlStateNormal]; [_downloadButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; [_downloadButton addTarget:self action:@selector(downloadAction) forControlEvents:UIControlEventTouchUpInside]; _downloadLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 150, W - 20, 40)]; _downloadLabel.font = [UIFont systemFontOfSize:14];; _downloadLabel.textColor = [UIColor darkTextColor]; _downloadLabel.opaque = NO; _downloadLabel.backgroundColor = [UIColor clearColor]; _downloadLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; _downloadLabel.numberOfLines = 2; _downloadProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; _downloadProgress.frame = CGRectMake(10, 190, W - 20, 30); _downloadProgress.hidden = YES; [_container addSubview:_nameLabel]; [_container addSubview:_sizeLabel]; [_container addSubview:_modifiedLabel]; [_container addSubview:_createdLabel]; [_container addSubview:_downloadButton]; [_container addSubview:_downloadLabel]; [_container addSubview:_downloadProgress]; } - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; const CGSize size = self.view.bounds.size; const CGFloat top = [self.topLayoutGuide length]; _container.frame = (CGRect){0, top, size.width, size.height - top}; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _nameLabel.text = _smbFile.path; _sizeLabel.text = [NSString stringWithFormat:@"size: %lld", _smbFile.stat.size]; _modifiedLabel.text = [NSString stringWithFormat:@"modified: %@", _smbFile.stat.lastModified]; _createdLabel.text = [NSString stringWithFormat:@"created: %@", _smbFile.stat.creationTime]; } - (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; //[self closeFiles]; } - (void) closeFiles { if (_fileHandle) { [_fileHandle closeFile]; _fileHandle = nil; } [_smbFile close]; } - (void) downloadAction { if (!_fileHandle) { NSString *folder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *filename = _smbFile.path.lastPathComponent; _filePath = [folder stringByAppendingPathComponent:filename]; NSFileManager *fm = [[NSFileManager alloc] init]; if ([fm fileExistsAtPath:_filePath]) [fm removeItemAtPath:_filePath error:nil]; [fm createFileAtPath:_filePath contents:nil attributes:nil]; NSError *error; _fileHandle = [NSFileHandle fileHandleForWritingToURL:[NSURL fileURLWithPath:_filePath] error:&error]; if (_fileHandle) { [_downloadButton setTitle:@"Cancel" forState:UIControlStateNormal]; _downloadLabel.text = @"starting .."; _downloadedBytes = 0; _downloadProgress.progress = 0; _downloadProgress.hidden = NO; _timestamp = [NSDate date]; [self download]; } else { _downloadLabel.text = [NSString stringWithFormat:@"failed: %@", error.localizedDescription]; } } else { [_downloadButton setTitle:@"Download" forState:UIControlStateNormal]; _downloadLabel.text = @"cancelled"; [self closeFiles]; } } -(void) updateDownloadStatus: (id) result { if ([result isKindOfClass:[NSError class]]) { NSError *error = result; [_downloadButton setTitle:@"Download" forState:UIControlStateNormal]; _downloadLabel.text = [NSString stringWithFormat:@"failed: %@", error.localizedDescription]; _downloadProgress.hidden = YES; [self closeFiles]; } else if ([result isKindOfClass:[NSData class]]) { NSData *data = result; if (data.length == 0) { [_downloadButton setTitle:@"Download" forState:UIControlStateNormal]; [self closeFiles]; } else { NSTimeInterval time = -[_timestamp timeIntervalSinceNow]; _downloadedBytes += data.length; _downloadProgress.progress = (float)_downloadedBytes / (float)_smbFile.stat.size; CGFloat value; NSString *unit; if (_downloadedBytes < 1024) { value = _downloadedBytes; unit = @"B"; } else if (_downloadedBytes < 1048576) { value = _downloadedBytes / 1024.f; unit = @"KB"; } else { value = _downloadedBytes / 1048576.f; unit = @"MB"; } _downloadLabel.text = [NSString stringWithFormat:@"downloaded %.1f%@ (%.1f%%) %.2f%@s", value, unit, _downloadProgress.progress * 100.f, value / time, unit]; if (_fileHandle) { [_fileHandle writeData:data]; if(_downloadedBytes == _smbFile.stat.size) { [self closeFiles]; [_downloadButton setTitle:@"Done" forState:UIControlStateNormal]; _downloadButton.enabled = NO; if ([QLPreviewController canPreviewItem:[NSURL fileURLWithPath:_filePath]]) { QLPreviewController *vc = [QLPreviewController new]; vc.delegate = self; vc.dataSource = self; [self.navigationController pushViewController:vc animated:YES]; } } else { [self download]; } } } } else { NSAssert(false, @"bugcheck"); } } - (void) download { __weak __typeof(self) weakSelf = self; [_smbFile readDataOfLength:1024*1024 block:^(id result) { FileViewController *p = weakSelf; //if (p && p.isViewLoaded && p.view.window) { if (p) { [p updateDownloadStatus:result]; } }]; } #pragma mark - QLPreviewController - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller { return 1; } - (id )previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index { return [NSURL fileURLWithPath:_filePath]; } @end ================================================ FILE: KxSMBSample/KxSMBSample-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier ru.kolyvan.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIFileSharingEnabled ================================================ FILE: KxSMBSample/KxSMBSample-Prefix.pch ================================================ // // Prefix header for all source files of the 'KxSMBSample' target in the 'KxSMBSample' project // #import #ifndef __IPHONE_4_0 #warning "This project uses features only available in iOS SDK 4.0 and later." #endif #ifdef __OBJC__ #import #import #endif ================================================ FILE: KxSMBSample/SmbAuthViewController.h ================================================ // // SmbAuthViewController.h // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 29.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @class SmbAuthViewController; @protocol SmbAuthViewControllerDelegate @optional - (void) couldSmbAuthViewController: (SmbAuthViewController *) controller done: (BOOL) done; @end @interface SmbAuthViewController : UIViewController @property (readwrite, nonatomic, weak) id delegate; @property (readwrite, nonatomic, strong) NSString *server; @property (readwrite, nonatomic, strong) NSString *workgroup; @property (readwrite, nonatomic, strong) NSString *username; @property (readwrite, nonatomic, strong) NSString *password; @end ================================================ FILE: KxSMBSample/SmbAuthViewController.m ================================================ // // SmbAuthViewController.m // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 29.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SmbAuthViewController.h" @interface SmbAuthViewController () @end @implementation SmbAuthViewController { UIView *_container; UILabel *_pathLabel; UITextField *_workgroupField; UITextField *_usernameField; UITextField *_passwordField; } - (id)init { self = [super initWithNibName:nil bundle:nil]; if (self) { self.title = NSLocalizedString(@"SMB Authorization", nil); } return self; } - (void)viewDidLoad { [super viewDidLoad]; const CGSize size = self.view.bounds.size; const CGFloat W = size.width; //const CGFloat H = size.height; _container = [[UIView alloc] initWithFrame:(CGRect){0,0,size}]; _container.autoresizingMask = UIViewAutoresizingNone; _container.backgroundColor = [UIColor whiteColor]; [self.view addSubview:_container]; _pathLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,10,W-20,30)]; _pathLabel.backgroundColor = [UIColor clearColor]; _pathLabel.textColor = [UIColor darkTextColor]; _pathLabel.font = [UIFont systemFontOfSize:16]; [_container addSubview:_pathLabel]; UILabel *workgroupLabel; workgroupLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,40,90,30)]; workgroupLabel.backgroundColor = [UIColor clearColor]; workgroupLabel.textColor = [UIColor darkTextColor]; workgroupLabel.font = [UIFont boldSystemFontOfSize:16]; workgroupLabel.text = NSLocalizedString(@"Workgroup", nil); [_container addSubview:workgroupLabel]; UILabel *usernameLabel; usernameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,90,90,30)]; usernameLabel.backgroundColor = [UIColor clearColor]; usernameLabel.textColor = [UIColor darkTextColor]; usernameLabel.font = [UIFont boldSystemFontOfSize:16]; usernameLabel.text = NSLocalizedString(@"Username", nil); [_container addSubview:usernameLabel]; UILabel *passwordLabel; passwordLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,140,90,30)]; passwordLabel.backgroundColor = [UIColor clearColor]; passwordLabel.textColor = [UIColor darkTextColor]; passwordLabel.font = [UIFont boldSystemFontOfSize:16]; passwordLabel.text = NSLocalizedString(@"Password", nil); [_container addSubview:passwordLabel]; _workgroupField = [[UITextField alloc] initWithFrame:CGRectMake(100, 41, W - 110, 30)]; _workgroupField.autocapitalizationType = UITextAutocapitalizationTypeNone; _workgroupField.autocorrectionType = UITextAutocorrectionTypeNo; _workgroupField.spellCheckingType = UITextSpellCheckingTypeNo; _workgroupField.autoresizingMask = UIViewAutoresizingFlexibleWidth; _workgroupField.clearButtonMode = UITextFieldViewModeWhileEditing; _workgroupField.textColor = [UIColor blueColor]; _workgroupField.font = [UIFont systemFontOfSize:16]; _workgroupField.borderStyle = UITextBorderStyleRoundedRect; _workgroupField.backgroundColor = [UIColor lightGrayColor]; _workgroupField.returnKeyType = UIReturnKeyNext; [_workgroupField addTarget:self action:@selector(textFieldDoneEditing:) forControlEvents:UIControlEventEditingDidEndOnExit]; [_container addSubview:_workgroupField]; _usernameField = [[UITextField alloc] initWithFrame:CGRectMake(100, 91, W - 110, 30)]; _usernameField.autocapitalizationType = UITextAutocapitalizationTypeNone; _usernameField.autocorrectionType = UITextAutocorrectionTypeNo; _usernameField.spellCheckingType = UITextSpellCheckingTypeNo; _usernameField.autoresizingMask = UIViewAutoresizingFlexibleWidth; _usernameField.clearButtonMode = UITextFieldViewModeWhileEditing; _usernameField.textColor = [UIColor blueColor]; _usernameField.font = [UIFont systemFontOfSize:16]; _usernameField.borderStyle = UITextBorderStyleRoundedRect; _usernameField.backgroundColor = [UIColor lightGrayColor]; _usernameField.returnKeyType = UIReturnKeyDone; [_usernameField addTarget:self action:@selector(textFieldDoneEditing:) forControlEvents:UIControlEventEditingDidEndOnExit]; [_container addSubview:_usernameField]; _passwordField = [[UITextField alloc] initWithFrame:CGRectMake(100, 141, W - 110, 30)]; _passwordField.autocapitalizationType = UITextAutocapitalizationTypeNone; _passwordField.autocorrectionType = UITextAutocorrectionTypeNo; _passwordField.spellCheckingType = UITextSpellCheckingTypeNo; _passwordField.autoresizingMask = UIViewAutoresizingFlexibleWidth; _passwordField.clearButtonMode = UITextFieldViewModeWhileEditing; _passwordField.textColor = [UIColor blueColor]; _passwordField.font = [UIFont systemFontOfSize:16]; _passwordField.borderStyle = UITextBorderStyleRoundedRect; _passwordField.backgroundColor = [UIColor lightGrayColor]; _passwordField.returnKeyType = UIReturnKeyDone; _passwordField.secureTextEntry = YES; [_passwordField addTarget:self action:@selector(textFieldDoneEditing:) forControlEvents:UIControlEventEditingDidEndOnExit]; [_container addSubview:_passwordField]; UIBarButtonItem *bbi; bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneAction)]; self.navigationItem.rightBarButtonItem = bbi; bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction)]; self.navigationItem.leftBarButtonItem = bbi; } - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; const CGSize size = self.view.bounds.size; const CGFloat top = [self.topLayoutGuide length]; _container.frame = (CGRect){0, top, size.width, size.height - top}; } - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _pathLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Server: %@", nil), _server]; _workgroupField.text = _workgroup; _usernameField.text = _username; _passwordField.text = _password; [_workgroupField becomeFirstResponder]; } - (void) textFieldDoneEditing: (id) sender { } - (void) cancelAction { __strong id p = self.delegate; if (p && [p respondsToSelector:@selector(couldSmbAuthViewController:done:)]) [p couldSmbAuthViewController:self done:NO]; } - (void) doneAction { _workgroup = _workgroupField.text; _username = _usernameField.text; _password = _passwordField.text; __strong id p = self.delegate; if (p && [p respondsToSelector:@selector(couldSmbAuthViewController:done:)]) [p couldSmbAuthViewController:self done:YES]; } @end ================================================ FILE: KxSMBSample/TreeViewController.h ================================================ // // TreeViewController.h // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 27.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @class KxSMBAuth; @interface TreeViewController : UITableViewController - (id)initAsHeadViewController; - (void) reloadPath; @property (readwrite, nonatomic, strong) NSString *path; @property (readwrite, nonatomic, strong) KxSMBAuth *defaultAuth; @end ================================================ FILE: KxSMBSample/TreeViewController.m ================================================ // // TreeViewController.m // kxsmb project // https://github.com/kolyvan/kxsmb/ // // Created by Kolyvan on 27.03.13. // /* Copyright (c) 2013 Konstantin Bukreev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "TreeViewController.h" #import "FileViewController.h" #import "KxSMBProvider.h" @interface TreeViewController () @end @implementation TreeViewController { BOOL _isHeadVC; NSArray *_items; BOOL _loading; BOOL _needNewPath; UITextField *_newPathField; } - (void) setPath:(NSString *)path { _path = path; [self reloadPath]; } - (id)init { self = [super init]; if (self) { self.title = @""; _needNewPath = YES; _isHeadVC = NO; } return self; } - (id)initAsHeadViewController { if((self = [self init])) { _isHeadVC = YES; } return self; } - (void)viewDidLoad { [super viewDidLoad]; if(NSClassFromString(@"UIRefreshControl")) { UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; [refreshControl addTarget:self action:@selector(reloadPath) forControlEvents:UIControlEventValueChanged]; self.refreshControl = refreshControl; } if(_isHeadVC) { self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(requestNewPath)]; } self.navigationItem.rightBarButtonItems = @[ [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(actionMkDir:)], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(actionCopyFile:)], ]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (self.navigationController.childViewControllers.count == 1 && _needNewPath) { _needNewPath = NO; [self requestNewPath]; } } - (void) reloadPath { NSString *path; if (_path.length) { path = _path; self.title = path.lastPathComponent; } else { path = @"smb://"; self.title = @"smb://"; } _items = nil; [self.tableView reloadData]; [self updateStatus:[NSString stringWithFormat: @"Fetching %@..", path]]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider fetchAtPath:path auth:_defaultAuth block:^(id result) { if ([result isKindOfClass:[NSError class]]) { [self updateStatus:result]; } else { [self updateStatus:nil]; if ([result isKindOfClass:[NSArray class]]) { _items = [result copy]; } else if ([result isKindOfClass:[KxSMBItem class]]) { _items = @[result]; } [self.tableView reloadData]; } }]; } - (void) requestNewPath { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Connect to Host" message:nil preferredStyle:UIAlertControllerStyleAlert]; [alert addTextFieldWithConfigurationHandler:^(UITextField * textField) { textField.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"LastServer"] ?: @"smb://"; textField.placeholder = @"smb://"; textField.clearButtonMode = UITextFieldViewModeAlways; }]; [alert addAction:[UIAlertAction actionWithTitle:@"Go" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { self.path = alert.textFields[0].text; [[NSUserDefaults standardUserDefaults] setObject:_newPathField.text forKey:@"LastServer"]; }]]; [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alert animated:YES completion:nil]; } - (void) updateStatus: (id) status { UIFont *font = [UIFont boldSystemFontOfSize:16]; if ([status isKindOfClass:[NSString class]]) { UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; CGSize sz = activityIndicator.frame.size; const float H = font.lineHeight + sz.height + 10; const float W = self.tableView.frame.size.width; UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, W, H)]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, W, font.lineHeight)]; label.text = status; label.font = font; label.textColor = [UIColor grayColor]; label.textAlignment = NSTextAlignmentCenter; label.opaque = NO; label.backgroundColor = [UIColor clearColor]; label.autoresizingMask = UIViewAutoresizingFlexibleWidth; [v addSubview:label]; if(![self.refreshControl isRefreshing]) [self.refreshControl beginRefreshing]; self.tableView.tableHeaderView = v; } else if ([status isKindOfClass:[NSError class]]) { const float W = self.tableView.frame.size.width; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, W, font.lineHeight)]; label.text = ((NSError *)status).localizedDescription; label.font = font; label.textColor = [UIColor redColor]; label.textAlignment = NSTextAlignmentCenter; label.opaque = NO; label.backgroundColor = [UIColor clearColor]; label.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.tableView.tableHeaderView = label; [self.refreshControl endRefreshing]; } else { self.tableView.tableHeaderView = nil; [self.refreshControl endRefreshing]; } } - (void) actionCopyFile:(id)sender { NSString *name = [NSString stringWithFormat:@"%u.tmp", (unsigned)[NSDate timeIntervalSinceReferenceDate]]; NSString *path = [_path stringByAppendingSMBPathComponent:name]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; [provider createFileAtPath:path overwrite:YES auth:_defaultAuth block:^(id result) { if ([result isKindOfClass:[KxSMBItemFile class]]) { NSData *data = [@"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." dataUsingEncoding:NSUTF8StringEncoding]; KxSMBItemFile *itemFile = result; [itemFile writeData:data block:^(id result) { NSLog(@"completed:%@", result); if (![result isKindOfClass:[NSError class]]) { [self reloadPath]; } }]; } else { NSLog(@"%@", result); } }]; } - (void) actionMkDir:(id)sender { NSString *path = [_path stringByAppendingSMBPathComponent:@"NewFolder"]; KxSMBProvider *provider = [KxSMBProvider sharedSmbProvider]; id result = [provider createFolderAtPath:path auth:_defaultAuth]; if ([result isKindOfClass:[KxSMBItemTree class]]) { NSMutableArray *ma = [_items mutableCopy]; [ma addObject:result]; _items = [ma copy]; [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:_items.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; } else { NSLog(@"%@", result); } } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _items.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; } KxSMBItem *item = _items[indexPath.row]; cell.textLabel.text = item.path.lastPathComponent; if ([item isKindOfClass:[KxSMBItemTree class]]) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.detailTextLabel.text = @""; } else { cell.accessoryType = UITableViewCellAccessoryNone; cell.detailTextLabel.text = [NSString stringWithFormat:@"%lld", item.stat.size]; } return cell; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; KxSMBItem *item = _items[indexPath.row]; if ([item isKindOfClass:[KxSMBItemTree class]]) { TreeViewController *vc = [[TreeViewController alloc] init]; vc.defaultAuth = _defaultAuth; vc.path = item.path; [self.navigationController pushViewController:vc animated:YES]; } else if ([item isKindOfClass:[KxSMBItemFile class]]) { FileViewController *vc = [[FileViewController alloc] init]; vc.smbFile = (KxSMBItemFile *)item; [self.navigationController pushViewController:vc animated:YES]; } } - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewCellEditingStyleDelete; } - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { KxSMBItem *item = _items[indexPath.row]; [[KxSMBProvider sharedSmbProvider] removeAtPath:item.path auth:_defaultAuth block:^(id result) { NSLog(@"completed:%@", result); if (![result isKindOfClass:[NSError class]]) { [self reloadPath]; } }]; } } #pragma mark - Alert view delegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 1) { self.path = _newPathField.text; [[NSUserDefaults standardUserDefaults] setObject:_newPathField.text forKey:@"LastServer"]; [[NSUserDefaults standardUserDefaults] synchronize]; } } @end ================================================ FILE: KxSMBSample/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: KxSMBSample/main.m ================================================ // // main.m // KxSMBSample // // Created by Kolyvan on 30.03.13. // Copyright (c) 2013 Konstantin Bukreev. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: KxSMBSample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 87085F8D1705D46C009CD258 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085F8C1705D46C009CD258 /* UIKit.framework */; }; 87085F8F1705D46C009CD258 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085F8E1705D46C009CD258 /* Foundation.framework */; }; 87085F911705D46C009CD258 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085F901705D46C009CD258 /* CoreGraphics.framework */; }; 87085F971705D46C009CD258 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 87085F951705D46C009CD258 /* InfoPlist.strings */; }; 87085F991705D46C009CD258 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085F981705D46C009CD258 /* main.m */; }; 87085F9D1705D46C009CD258 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085F9C1705D46C009CD258 /* AppDelegate.m */; }; 87085F9F1705D46C009CD258 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 87085F9E1705D46C009CD258 /* Default.png */; }; 87085FA11705D46C009CD258 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 87085FA01705D46C009CD258 /* Default@2x.png */; }; 87085FA31705D46C009CD258 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 87085FA21705D46C009CD258 /* Default-568h@2x.png */; }; 87085FB41705D494009CD258 /* KxSMBProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085FB31705D494009CD258 /* KxSMBProvider.m */; }; 87085FBB1705D4AB009CD258 /* FileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085FB61705D4AB009CD258 /* FileViewController.m */; }; 87085FBC1705D4AB009CD258 /* SmbAuthViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085FB81705D4AB009CD258 /* SmbAuthViewController.m */; }; 87085FBD1705D4AB009CD258 /* TreeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085FBA1705D4AB009CD258 /* TreeViewController.m */; }; 87085FC41705D4CA009CD258 /* libsmbclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FBF1705D4CA009CD258 /* libsmbclient.a */; }; 87085FC51705D4CA009CD258 /* libtalloc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FC11705D4CA009CD258 /* libtalloc.a */; }; 87085FC61705D4CA009CD258 /* libtdb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FC21705D4CA009CD258 /* libtdb.a */; }; 87085FC71705D4CA009CD258 /* libwbclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FC31705D4CA009CD258 /* libwbclient.a */; }; 87085FC91705D4FD009CD258 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FC81705D4FD009CD258 /* libz.dylib */; }; 87085FCB1705D503009CD258 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FCA1705D503009CD258 /* libiconv.dylib */; }; 87085FCD1705D509009CD258 /* libresolv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085FCC1705D509009CD258 /* libresolv.dylib */; }; 871CC5751774589C00EDD76D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87085F8E1705D46C009CD258 /* Foundation.framework */; }; 871CC580177458AD00EDD76D /* KxSMBProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 87085FB31705D494009CD258 /* KxSMBProvider.m */; }; EAC6EFEE179A4133005AC807 /* libtevent.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EAC6EFED179A4133005AC807 /* libtevent.a */; }; F992AF0C1BC3D3CC0063A4B3 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F992AF0B1BC3D3CC0063A4B3 /* QuickLook.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 871CC5721774589C00EDD76D /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/${PRODUCT_NAME}"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 87085F891705D46C009CD258 /* KxSMBSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KxSMBSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 87085F8C1705D46C009CD258 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 87085F8E1705D46C009CD258 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 87085F901705D46C009CD258 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 87085F941705D46C009CD258 /* KxSMBSample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KxSMBSample-Info.plist"; sourceTree = ""; }; 87085F961705D46C009CD258 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 87085F981705D46C009CD258 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 87085F9A1705D46C009CD258 /* KxSMBSample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KxSMBSample-Prefix.pch"; sourceTree = ""; }; 87085F9B1705D46C009CD258 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 87085F9C1705D46C009CD258 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 87085F9E1705D46C009CD258 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 87085FA01705D46C009CD258 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 87085FA21705D46C009CD258 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 87085FB21705D494009CD258 /* KxSMBProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KxSMBProvider.h; sourceTree = ""; }; 87085FB31705D494009CD258 /* KxSMBProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KxSMBProvider.m; sourceTree = ""; }; 87085FB51705D4AB009CD258 /* FileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FileViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 87085FB61705D4AB009CD258 /* FileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileViewController.m; sourceTree = ""; }; 87085FB71705D4AB009CD258 /* SmbAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmbAuthViewController.h; sourceTree = ""; }; 87085FB81705D4AB009CD258 /* SmbAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SmbAuthViewController.m; sourceTree = ""; }; 87085FB91705D4AB009CD258 /* TreeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TreeViewController.h; sourceTree = ""; }; 87085FBA1705D4AB009CD258 /* TreeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TreeViewController.m; sourceTree = ""; }; 87085FBF1705D4CA009CD258 /* libsmbclient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsmbclient.a; path = libs/libsmbclient.a; sourceTree = ""; }; 87085FC01705D4CA009CD258 /* libsmbclient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libsmbclient.h; path = libs/libsmbclient.h; sourceTree = ""; }; 87085FC11705D4CA009CD258 /* libtalloc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalloc.a; path = libs/libtalloc.a; sourceTree = ""; }; 87085FC21705D4CA009CD258 /* libtdb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtdb.a; path = libs/libtdb.a; sourceTree = ""; }; 87085FC31705D4CA009CD258 /* libwbclient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libwbclient.a; path = libs/libwbclient.a; sourceTree = ""; }; 87085FC81705D4FD009CD258 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 87085FCA1705D503009CD258 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; 87085FCC1705D509009CD258 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = usr/lib/libresolv.dylib; sourceTree = SDKROOT; }; 871CC5581774489A00EDD76D /* talloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talloc.h; path = libs/talloc.h; sourceTree = ""; }; 871CC5591774491A00EDD76D /* talloc_stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talloc_stack.h; path = libs/talloc_stack.h; sourceTree = ""; }; 871CC5741774589C00EDD76D /* libKxSMB.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKxSMB.a; sourceTree = BUILT_PRODUCTS_DIR; }; 871CC5781774589C00EDD76D /* KxSMB-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KxSMB-Prefix.pch"; sourceTree = ""; }; EAC6EFED179A4133005AC807 /* libtevent.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtevent.a; path = libs/libtevent.a; sourceTree = ""; }; F992AF0B1BC3D3CC0063A4B3 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 87085F861705D46C009CD258 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( F992AF0C1BC3D3CC0063A4B3 /* QuickLook.framework in Frameworks */, 87085FCD1705D509009CD258 /* libresolv.dylib in Frameworks */, 87085FCB1705D503009CD258 /* libiconv.dylib in Frameworks */, 87085FC91705D4FD009CD258 /* libz.dylib in Frameworks */, 87085F8D1705D46C009CD258 /* UIKit.framework in Frameworks */, 87085F8F1705D46C009CD258 /* Foundation.framework in Frameworks */, 87085F911705D46C009CD258 /* CoreGraphics.framework in Frameworks */, EAC6EFEE179A4133005AC807 /* libtevent.a in Frameworks */, 87085FC41705D4CA009CD258 /* libsmbclient.a in Frameworks */, 87085FC51705D4CA009CD258 /* libtalloc.a in Frameworks */, 87085FC61705D4CA009CD258 /* libtdb.a in Frameworks */, 87085FC71705D4CA009CD258 /* libwbclient.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 871CC5711774589C00EDD76D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 871CC5751774589C00EDD76D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 87085F801705D46B009CD258 = { isa = PBXGroup; children = ( 87085FB21705D494009CD258 /* KxSMBProvider.h */, 87085FB31705D494009CD258 /* KxSMBProvider.m */, 87085FBE1705D4BE009CD258 /* libs */, 87085F921705D46C009CD258 /* KxSMBSample */, 871CC5761774589C00EDD76D /* KxSMB */, 87085F8B1705D46C009CD258 /* Frameworks */, 87085F8A1705D46C009CD258 /* Products */, ); sourceTree = ""; }; 87085F8A1705D46C009CD258 /* Products */ = { isa = PBXGroup; children = ( 87085F891705D46C009CD258 /* KxSMBSample.app */, 871CC5741774589C00EDD76D /* libKxSMB.a */, ); name = Products; sourceTree = ""; }; 87085F8B1705D46C009CD258 /* Frameworks */ = { isa = PBXGroup; children = ( F992AF0B1BC3D3CC0063A4B3 /* QuickLook.framework */, 87085FCC1705D509009CD258 /* libresolv.dylib */, 87085FCA1705D503009CD258 /* libiconv.dylib */, 87085FC81705D4FD009CD258 /* libz.dylib */, 87085F8C1705D46C009CD258 /* UIKit.framework */, 87085F8E1705D46C009CD258 /* Foundation.framework */, 87085F901705D46C009CD258 /* CoreGraphics.framework */, ); name = Frameworks; sourceTree = ""; }; 87085F921705D46C009CD258 /* KxSMBSample */ = { isa = PBXGroup; children = ( 87085FB51705D4AB009CD258 /* FileViewController.h */, 87085FB61705D4AB009CD258 /* FileViewController.m */, 87085FB71705D4AB009CD258 /* SmbAuthViewController.h */, 87085FB81705D4AB009CD258 /* SmbAuthViewController.m */, 87085FB91705D4AB009CD258 /* TreeViewController.h */, 87085FBA1705D4AB009CD258 /* TreeViewController.m */, 87085F9B1705D46C009CD258 /* AppDelegate.h */, 87085F9C1705D46C009CD258 /* AppDelegate.m */, 87085F931705D46C009CD258 /* Supporting Files */, ); path = KxSMBSample; sourceTree = ""; }; 87085F931705D46C009CD258 /* Supporting Files */ = { isa = PBXGroup; children = ( 87085F941705D46C009CD258 /* KxSMBSample-Info.plist */, 87085F951705D46C009CD258 /* InfoPlist.strings */, 87085F981705D46C009CD258 /* main.m */, 87085F9A1705D46C009CD258 /* KxSMBSample-Prefix.pch */, 87085F9E1705D46C009CD258 /* Default.png */, 87085FA01705D46C009CD258 /* Default@2x.png */, 87085FA21705D46C009CD258 /* Default-568h@2x.png */, ); name = "Supporting Files"; sourceTree = ""; }; 87085FBE1705D4BE009CD258 /* libs */ = { isa = PBXGroup; children = ( 871CC5591774491A00EDD76D /* talloc_stack.h */, 871CC5581774489A00EDD76D /* talloc.h */, 87085FC01705D4CA009CD258 /* libsmbclient.h */, 87085FBF1705D4CA009CD258 /* libsmbclient.a */, 87085FC11705D4CA009CD258 /* libtalloc.a */, 87085FC21705D4CA009CD258 /* libtdb.a */, 87085FC31705D4CA009CD258 /* libwbclient.a */, EAC6EFED179A4133005AC807 /* libtevent.a */, ); name = libs; sourceTree = ""; }; 871CC5761774589C00EDD76D /* KxSMB */ = { isa = PBXGroup; children = ( 871CC5771774589C00EDD76D /* Supporting Files */, ); path = KxSMB; sourceTree = ""; }; 871CC5771774589C00EDD76D /* Supporting Files */ = { isa = PBXGroup; children = ( 871CC5781774589C00EDD76D /* KxSMB-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 87085F881705D46C009CD258 /* KxSMBSample */ = { isa = PBXNativeTarget; buildConfigurationList = 87085FAF1705D46C009CD258 /* Build configuration list for PBXNativeTarget "KxSMBSample" */; buildPhases = ( 87085F851705D46C009CD258 /* Sources */, 87085F861705D46C009CD258 /* Frameworks */, 87085F871705D46C009CD258 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = KxSMBSample; productName = KxSMBSample; productReference = 87085F891705D46C009CD258 /* KxSMBSample.app */; productType = "com.apple.product-type.application"; }; 871CC5731774589C00EDD76D /* KxSMB */ = { isa = PBXNativeTarget; buildConfigurationList = 871CC57F1774589C00EDD76D /* Build configuration list for PBXNativeTarget "KxSMB" */; buildPhases = ( 871CC5701774589C00EDD76D /* Sources */, 871CC5711774589C00EDD76D /* Frameworks */, 871CC5721774589C00EDD76D /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = KxSMB; productName = KxSMB; productReference = 871CC5741774589C00EDD76D /* libKxSMB.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 87085F811705D46B009CD258 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0460; ORGANIZATIONNAME = "Konstantin Bukreev"; }; buildConfigurationList = 87085F841705D46B009CD258 /* Build configuration list for PBXProject "KxSMBSample" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 87085F801705D46B009CD258; productRefGroup = 87085F8A1705D46C009CD258 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 87085F881705D46C009CD258 /* KxSMBSample */, 871CC5731774589C00EDD76D /* KxSMB */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 87085F871705D46C009CD258 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 87085F971705D46C009CD258 /* InfoPlist.strings in Resources */, 87085F9F1705D46C009CD258 /* Default.png in Resources */, 87085FA11705D46C009CD258 /* Default@2x.png in Resources */, 87085FA31705D46C009CD258 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 87085F851705D46C009CD258 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 87085F991705D46C009CD258 /* main.m in Sources */, 87085F9D1705D46C009CD258 /* AppDelegate.m in Sources */, 87085FB41705D494009CD258 /* KxSMBProvider.m in Sources */, 87085FBB1705D4AB009CD258 /* FileViewController.m in Sources */, 87085FBC1705D4AB009CD258 /* SmbAuthViewController.m in Sources */, 87085FBD1705D4AB009CD258 /* TreeViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 871CC5701774589C00EDD76D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 871CC580177458AD00EDD76D /* KxSMBProvider.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 87085F951705D46C009CD258 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 87085F961705D46C009CD258 /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 87085FAD1705D46C009CD258 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 87085FAE1705D46C009CD258 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 87085FB01705D46C009CD258 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { DEBUG_INFORMATION_FORMAT = dwarf; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KxSMBSample/KxSMBSample-Prefix.pch"; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; INFOPLIST_FILE = "KxSMBSample/KxSMBSample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/libs\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; USER_HEADER_SEARCH_PATHS = ""; WRAPPER_EXTENSION = app; }; name = Debug; }; 87085FB11705D46C009CD258 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { DEBUG_INFORMATION_FORMAT = dwarf; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KxSMBSample/KxSMBSample-Prefix.pch"; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; INFOPLIST_FILE = "KxSMBSample/KxSMBSample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/libs\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; USER_HEADER_SEARCH_PATHS = ""; WRAPPER_EXTENSION = app; }; name = Release; }; 871CC57D1774589C00EDD76D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { DSTROOT = /tmp/KxSMB.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KxSMB/KxSMB-Prefix.pch"; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; }; name = Debug; }; 871CC57E1774589C00EDD76D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { DSTROOT = /tmp/KxSMB.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KxSMB/KxSMB-Prefix.pch"; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/libs\""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 87085F841705D46B009CD258 /* Build configuration list for PBXProject "KxSMBSample" */ = { isa = XCConfigurationList; buildConfigurations = ( 87085FAD1705D46C009CD258 /* Debug */, 87085FAE1705D46C009CD258 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 87085FAF1705D46C009CD258 /* Build configuration list for PBXNativeTarget "KxSMBSample" */ = { isa = XCConfigurationList; buildConfigurations = ( 87085FB01705D46C009CD258 /* Debug */, 87085FB11705D46C009CD258 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 871CC57F1774589C00EDD76D /* Build configuration list for PBXNativeTarget "KxSMB" */ = { isa = XCConfigurationList; buildConfigurations = ( 871CC57D1774589C00EDD76D /* Debug */, 871CC57E1774589C00EDD76D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 87085F811705D46B009CD258 /* Project object */; } ================================================ FILE: LICENSE ================================================ Copyright (c) 2013 Konstantin Bukreev. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Rakefile ================================================ # # Rakefile # kxsmb project # https://github.com/kolyvan/kxsmb/ # # Created by Kolyvan on 29.03.13. # # # Copyright (c) 2013 Konstantin Bukreev All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "pathname" require "fileutils" # utils def system_or_exit(cmd, stdout = nil) puts "Executing #{cmd}" cmd += " >#{stdout}" if stdout system(cmd) or raise "******** Build failed ********" end def copyIfNotExists(file, from, to) dest = Pathname.new(to) dest.mkdir unless dest.exist? unless (dest + file).exist? source = Pathname.new(from) + file FileUtils.copy source, dest p "copy #{source} -> #{dest}" end end def cleanOrMkDir(path) dest = Pathname.new path if dest.exist? FileUtils.rm Dir.glob("#{path}/*.a") else dest.mkdir end end def cleanDir(path) dest = Pathname.new path if dest.exist? FileUtils.rm Dir.glob("#{path}/*.a") end end # versions IOS_MIN_VERSION='8.0' SAMBA_VERSION='4.0.26' # samba source SAMBA_BASE_URL="http://ftp.samba.org/pub/samba/stable/" #pathes XCODE_PATH=%x{ /usr/bin/xcode-select --print-path }.delete("\n") SIM_SDK_PATH=XCODE_PATH + "/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk" IOS_SDK_PATH=XCODE_PATH + "/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk" #SAMBA_PATH="samba-#{SAMBA_VERSION}/source3" SAMBA_FOLDER="samba" SAMBA_SOURCE_PATH="#{SAMBA_FOLDER}/source3" EXT_INCLUDE_PATH='tmp/include' # configure arguments CF_FLAGS='-pipe -Wno-trigraphs -fpascal-strings -Os -fembed-bitcode -g' IOS_CF_FLAGS='-ftree-vectorize' IOS_LD_FLAGS='' ARM7_CF_FLAGS="-arch armv7 -mcpu=cortex-a8 -mfpu=neon #{IOS_CF_FLAGS} #{CF_FLAGS}" ARM7_LD_FLAGS="-arch armv7 #{IOS_LD_FLAGS}" ARM7s_CF_FLAGS="-arch armv7s -mcpu=cortex-a8 -mfpu=neon #{IOS_CF_FLAGS} #{CF_FLAGS}" ARM7s_LD_FLAGS="-arch armv7s #{IOS_LD_FLAGS}" ARM64_CF_FLAGS="-arch arm64 -Wno-error=implicit-function-declaration #{IOS_CF_FLAGS} #{CF_FLAGS}" ARM64_LD_FLAGS="-arch arm64 #{IOS_LD_FLAGS}" I386_CF_FLAGS="-arch i386 #{CF_FLAGS}" I386_LD_FLAGS='-arch i386' X86_64_CF_FLAGS="-arch x86_64 -Wno-error=implicit-function-declaration #{CF_FLAGS}" X86_64_LD_FLAGS='-arch x86_64' SMB_ARGS = [ '--prefix=/private', '--disable-shared', '--enable-static', '--without-readline', '--with-libsmbclient', '--without-libnetapi', '--without-libsmbsharemodes', '--without-cluster-support', '--without-ldap', '--disable-swat', '--disable-cups', '--disable-iprint', 'libreplace_cv_HAVE_C99_VSNPRINTF=yes', 'samba_cv_CC_NEGATIVE_ENUM_VALUES=yes', ] SIM_SMB_ARGS = [ '--enable-debug', 'samba_cv_HAVE_FCNTL_LOCK=yes' ] IOS_SMB_ARGS = [ 'ac_cv_header_libunwind_h=no', 'ac_cv_header_execinfo_h=no', 'ac_cv_header_rpcsvc_ypclnt_h=no', 'ac_cv_file__proc_sys_kernel_core_pattern=no', 'ac_cv_func_fdatasync=no', 'libreplace_cv_HAVE_GETADDRINFO=no', 'samba_cv_SYSCONF_SC_NPROCESSORS_ONLN=no', 'samba_cv_big_endian=no', 'samba_cv_little_endian=yes', ] ARM7_SMB_ARGS = [ '--host=arm-apple-darwin', ] ARM7s_SMB_ARGS = [ '--host=arm-apple-darwin', ] ARM64_SMB_ARGS = [ '--host=arm-apple-darwin', ] I386_SMB_ARGS = [ '--host=i686-apple-darwin', ] X86_64_SMB_ARGS = [ '--host=x86_64-apple-darwin', ] # libs SMB_LIBS = [ 'libsmbclient', 'libtalloc', 'libtevent', 'libtdb', 'libwbclient', ] # functions def mkArgs(sdkPath, platformArgs, procArgs, cfFlags, ldFlags) extInclude = Pathname.new(EXT_INCLUDE_PATH).realpath args = SMB_ARGS + platformArgs + procArgs ENV['AR']="xcrun ar" ENV['CC']="xcrun clang" ENV['CPP']="xcrun clang -E" ENV['LD']="xcrun ld" ENV['CFLAGS']="-std=gnu99 -no-cpp-precomp -miphoneos-version-min=#{IOS_MIN_VERSION} -isysroot #{sdkPath} -I#{sdkPath}/usr/include #{cfFlags}" ENV['CPPFLAGS']="-std=gnu99 -no-cpp-precomp -miphoneos-version-min=#{IOS_MIN_VERSION} -isysroot #{sdkPath} -I#{sdkPath}/usr/include #{cfFlags} -I#{extInclude}" ENV['LDFLAGS']="-miphoneos-version-min=#{IOS_MIN_VERSION} -isysroot #{sdkPath} -L#{sdkPath}/usr/lib #{ldFlags}" args.join(' ') end def buildArch(arch) case arch when 'i386' args = mkArgs(SIM_SDK_PATH, SIM_SMB_ARGS, I386_SMB_ARGS, I386_CF_FLAGS, I386_LD_FLAGS) when 'armv7' args = mkArgs(IOS_SDK_PATH, IOS_SMB_ARGS, ARM7_SMB_ARGS, ARM7_CF_FLAGS, ARM7_LD_FLAGS) when 'armv7s' args = mkArgs(IOS_SDK_PATH, IOS_SMB_ARGS, ARM7s_SMB_ARGS, ARM7s_CF_FLAGS, ARM7s_LD_FLAGS) when 'arm64' args = mkArgs(IOS_SDK_PATH, IOS_SMB_ARGS, ARM64_SMB_ARGS, ARM64_CF_FLAGS, ARM64_LD_FLAGS) when 'x86_64' args = mkArgs(SIM_SDK_PATH, SIM_SMB_ARGS, X86_64_SMB_ARGS, X86_64_CF_FLAGS, X86_64_LD_FLAGS) else raise "Build failed: unknown arch: #{arch}" end p args system_or_exit "cd #{SAMBA_SOURCE_PATH}; ./autogen.sh" system_or_exit "cd #{SAMBA_SOURCE_PATH}; ./configure #{args}" SMB_LIBS.each do |x| system_or_exit "cd #{SAMBA_SOURCE_PATH}; make #{x}" end dest = Pathname.new("#{SAMBA_SOURCE_PATH}/bin/#{arch}") cleanOrMkDir(dest) SMB_LIBS.each do |x| FileUtils.move Pathname.new("#{SAMBA_SOURCE_PATH}/bin/#{x}.a"), dest end system_or_exit "cd #{SAMBA_SOURCE_PATH}; make clean" end def checkExtInclude extInclude = Pathname.new(EXT_INCLUDE_PATH) extInclude.mkpath unless extInclude.exist? copyIfNotExists('crt_externs.h', "#{SIM_SDK_PATH}/usr/include/", extInclude.realpath) end # tasks desc "Build smb armv7 libs" task :build_smb_armv7 do checkExtInclude buildArch('armv7') end desc "Build smb armv7s libs" task :build_smb_armv7s do checkExtInclude buildArch('armv7s') end desc "Build smb arm64 libs" task :build_smb_arm64 do checkExtInclude buildArch('arm64') end desc "Build smb i386 libs" task :build_smb_i386 do buildArch('i386') end desc "Build smb x86_64 libs" task :build_smb_x86_64 do buildArch('x86_64') end desc "Build smb universal libs (full)" task :build_smb_universal_full do dest = Pathname.new("#{SAMBA_SOURCE_PATH}/bin/universal") dest.mkdir unless dest.exist? SMB_LIBS.each do |x| args = "-create -arch armv7 #{SAMBA_SOURCE_PATH}/bin/armv7/#{x}.a -arch armv7s #{SAMBA_SOURCE_PATH}/bin/armv7s/#{x}.a -arch arm64 #{SAMBA_SOURCE_PATH}/bin/arm64/#{x}.a -arch i386 #{SAMBA_SOURCE_PATH}/bin/i386/#{x}.a -arch x86_64 #{SAMBA_SOURCE_PATH}/bin/x86_64/#{x}.a -output #{dest}/#{x}.a" system_or_exit "xcrun lipo #{args}" end end desc "Build smb universal libs" task :build_smb_universal do dest = Pathname.new("#{SAMBA_SOURCE_PATH}/bin/universal") dest.mkdir unless dest.exist? SMB_LIBS.each do |x| args = "-create -arch armv7 #{SAMBA_SOURCE_PATH}/bin/armv7/#{x}.a -arch arm64 #{SAMBA_SOURCE_PATH}/bin/arm64/#{x}.a -arch i386 #{SAMBA_SOURCE_PATH}/bin/i386/#{x}.a -arch x86_64 #{SAMBA_SOURCE_PATH}/bin/x86_64/#{x}.a -output #{dest}/#{x}.a" system_or_exit "xcrun lipo #{args}" end end desc "Copy smb headers" task :copy_headers do copyIfNotExists('libsmbclient.h', "#{SAMBA_SOURCE_PATH}/include/", 'libs') copyIfNotExists('talloc.h', "#{SAMBA_FOLDER}/lib/talloc/", 'libs') copyIfNotExists('talloc_stack.h', "#{SAMBA_FOLDER}/lib/util/", 'libs') end desc "Copy smb libs" task :copy_libs do dest = Pathname.new('libs') dest.mkdir unless dest.exist? from = Pathname.new("#{SAMBA_SOURCE_PATH}/bin/universal") SMB_LIBS.each do |x| source = from + "#{x}.a" FileUtils.move source, dest p "copy #{source} -> #{dest}" end end desc "Clean" task :clean do cleanDir("#{SAMBA_SOURCE_PATH}/bin/armv7") cleanDir("#{SAMBA_SOURCE_PATH}/bin/armv7s") cleanDir("#{SAMBA_SOURCE_PATH}/bin/arm64") cleanDir("#{SAMBA_SOURCE_PATH}/bin/i386") cleanDir("#{SAMBA_SOURCE_PATH}/bin/x86_64") cleanDir("#{SAMBA_SOURCE_PATH}/bin/universal") system_or_exit "cd #{SAMBA_SOURCE_PATH}; make clean" end desc "Retrieve samble archive" task :retrieve_samba do p = Pathname.new "#{SAMBA_SOURCE_PATH}" unless p.exist? name = "samba-#{SAMBA_VERSION}" file = "#{name}.tar.gz" url = "#{SAMBA_BASE_URL}#{file}" p "retrieving samba from #{url}" system_or_exit "/usr/bin/curl -L --output #{file} #{url}" p "extracting samba from archive" system_or_exit "tar -zxf #{file}" Pathname.new(file).delete Pathname.new(name).rename SAMBA_FOLDER end end task :build_full => [:retrieve_samba, :build_smb_armv7, :build_smb_armv7s, :build_smb_arm64, :build_smb_i386, :build_smb_x86_64, :build_smb_universal_full, :copy_libs, :copy_headers] task :build_all => [:retrieve_samba, :build_smb_armv7, :build_smb_arm64, :build_smb_i386, :build_smb_x86_64, :build_smb_universal, :copy_libs, :copy_headers] task :default => [:build_all] ================================================ FILE: readme.md ================================================ KxSMB is objective-c wrapper for libsmbclient lib. =========================================== For now KxSMB supports a limited set of SMB operations. It mostly was designed for browsing local net and retrieving files. ### Build instructions: First you need download, configure and build [samba](http://www.samba.org). For this open console and type in cd kxsmb rake ### Usage 1. Drop files from kxsmb/libs folder in your project. 2. Add libs: libz.dylib, libresolv.dylib and liconv.dylib. Fetching a folder content: NSArray *items = [[KxSMBProvider sharedSmbProvider] fetchAtPath: @"smb://server/share/"]; Reading a file: KxSMBItemFile *file = [[KxSMBProvider sharedSmbProvider] fetchAtPath: @"smb://server/share/file"]; NSData *data = [file readDataToEndOfFile]; Look at kxSMBSample demo project as example of using. ### Requirements at least iOS 5.0 and Xcode 4.5.0 ### License kxsmb is open source and covered by a standard 2-clause BSD license. See the LICENSE file for more info. [Samba](http://www.samba.org) is [Free Software](http://www.gnu.org/philosophy/free-sw.html) licensed under the [GNU General Public License](http://www.samba.org/samba/docs/GPL.html). ### Feedback Tweet me — [@kolyvan_ru](http://twitter.com/kolyvan_ru).