Full Code of kolyvan/kxsmb for AI

master 30fc7b8d043c cached
20 files
174.6 KB
43.7k tokens
2 symbols
1 requests
Download .txt
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 <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>

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 <NSObject>

- (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<KxSMBProviderDelegate> 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:@"<smb %@ '%@' %lld>",
            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<KxSMBProviderDelegate> 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 <UIKit/UIKit.h>


@interface AppDelegate : UIResponder <UIApplicationDelegate>

@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() <KxSMBProviderDelegate, SmbAuthViewControllerDelegate>
@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 <UIKit/UIKit.h>

@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 <QuickLook/QuickLook.h>

@interface FileViewController () <QLPreviewControllerDelegate, QLPreviewControllerDataSource>
@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 <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index
{
    return [NSURL fileURLWithPath:_filePath];
}

@end


================================================
FILE: KxSMBSample/KxSMBSample-Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>ru.kolyvan.${PRODUCT_NAME:rfc1034identifier}</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UIFileSharingEnabled</key>
	<false/>
</dict>
</plist>


================================================
FILE: KxSMBSample/KxSMBSample-Prefix.pch
================================================
//
// Prefix header for all source files of the 'KxSMBSample' target in the 'KxSMBSample' project
//

#import <Availability.h>

#ifndef __IPHONE_4_0
#warning "This project uses features only available in iOS SDK 4.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#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 <UIKit/UIKit.h>

@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 <UIKit/UIKit.h>

@class KxSMBAuth;

@interface TreeViewController : UITableViewController <UIAlertViewDelegate>
- (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 () <UITableViewDataSource, UITableViewDelegate>
@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 <UIKit/UIKit.h>

#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 = "<group>"; };
		87085F961705D46C009CD258 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
		87085F981705D46C009CD258 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		87085F9A1705D46C009CD258 /* KxSMBSample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KxSMBSample-Prefix.pch"; sourceTree = "<group>"; };
		87085F9B1705D46C009CD258 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
		87085F9C1705D46C009CD258 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
		87085F9E1705D46C009CD258 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
		87085FA01705D46C009CD258 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
		87085FA21705D46C009CD258 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
		87085FB21705D494009CD258 /* KxSMBProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KxSMBProvider.h; sourceTree = "<group>"; };
		87085FB31705D494009CD258 /* KxSMBProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KxSMBProvider.m; sourceTree = "<group>"; };
		87085FB51705D4AB009CD258 /* FileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FileViewController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
		87085FB61705D4AB009CD258 /* FileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileViewController.m; sourceTree = "<group>"; };
		87085FB71705D4AB009CD258 /* SmbAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmbAuthViewController.h; sourceTree = "<group>"; };
		87085FB81705D4AB009CD258 /* SmbAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SmbAuthViewController.m; sourceTree = "<group>"; };
		87085FB91705D4AB009CD258 /* TreeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TreeViewController.h; sourceTree = "<group>"; };
		87085FBA1705D4AB009CD258 /* TreeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TreeViewController.m; sourceTree = "<group>"; };
		87085FBF1705D4CA009CD258 /* libsmbclient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsmbclient.a; path = libs/libsmbclient.a; sourceTree = "<group>"; };
		87085FC01705D4CA009CD258 /* libsmbclient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libsmbclient.h; path = libs/libsmbclient.h; sourceTree = "<group>"; };
		87085FC11705D4CA009CD258 /* libtalloc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalloc.a; path = libs/libtalloc.a; sourceTree = "<group>"; };
		87085FC21705D4CA009CD258 /* libtdb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtdb.a; path = libs/libtdb.a; sourceTree = "<group>"; };
		87085FC31705D4CA009CD258 /* libwbclient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libwbclient.a; path = libs/libwbclient.a; sourceTree = "<group>"; };
		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 = "<group>"; };
		871CC5591774491A00EDD76D /* talloc_stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talloc_stack.h; path = libs/talloc_stack.h; sourceTree = "<group>"; };
		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 = "<group>"; };
		EAC6EFED179A4133005AC807 /* libtevent.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtevent.a; path = libs/libtevent.a; sourceTree = "<group>"; };
		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 = "<group>";
		};
		87085F8A1705D46C009CD258 /* Products */ = {
			isa = PBXGroup;
			children = (
				87085F891705D46C009CD258 /* KxSMBSample.app */,
				871CC5741774589C00EDD76D /* libKxSMB.a */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		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 = "<group>";
		};
		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 = "<group>";
		};
		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 = "<group>";
		};
		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 = "<group>";
		};
		871CC5761774589C00EDD76D /* KxSMB */ = {
			isa = PBXGroup;
			children = (
				871CC5771774589C00EDD76D /* Supporting Files */,
			);
			path = KxSMB;
			sourceTree = "<group>";
		};
		871CC5771774589C00EDD76D /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				871CC5781774589C00EDD76D /* KxSMB-Prefix.pch */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
/* 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 = "<group>";
		};
/* 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).
Download .txt
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
Download .txt
SYMBOL INDEX (2 symbols across 1 files)

FILE: KxSMBProvider.h
  type KxSMBError (line 38) | typedef enum {
  type KxSMBItemType (line 58) | typedef enum {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (187K chars).
[
  {
    "path": ".gitignore",
    "chars": 285,
    "preview": "\n## xcode specific\nbuild/*\n*.pbxuser\n*.mode2v3\n*.mode1v3\n*.perspective\n*.perspectivev3\n*~.nib\n\n## ignore private workspa"
  },
  {
    "path": "KxSMB/KxSMB-Prefix.pch",
    "chars": 151,
    "preview": "//\n// Prefix header for all source files of the 'KxSMB' target in the 'KxSMB' project\n//\n\n#ifdef __OBJC__\n    #import <F"
  },
  {
    "path": "KxSMBProvider.h",
    "chars": 12404,
    "preview": "//\n//  KxSambaProvider.h\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 28.03.13.\n"
  },
  {
    "path": "KxSMBProvider.m",
    "chars": 81104,
    "preview": "//\n//  KxSambaProvider.m\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 28.03.13.\n"
  },
  {
    "path": "KxSMBSample/AppDelegate.h",
    "chars": 1585,
    "preview": "//\n//  AppDelegate.h\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 27.03.13.\n//\n\n"
  },
  {
    "path": "KxSMBSample/AppDelegate.m",
    "chars": 5756,
    "preview": "//\n//  AppDelegate.m\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 27.03.13.\n//\n\n"
  },
  {
    "path": "KxSMBSample/FileViewController.h",
    "chars": 1617,
    "preview": "//\n//  FileViewController.h\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 29.03.1"
  },
  {
    "path": "KxSMBSample/FileViewController.m",
    "chars": 11421,
    "preview": "//\n//  FileViewController.m\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 29.03.1"
  },
  {
    "path": "KxSMBSample/KxSMBSample-Info.plist",
    "chars": 1500,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "KxSMBSample/KxSMBSample-Prefix.pch",
    "chars": 325,
    "preview": "//\n// Prefix header for all source files of the 'KxSMBSample' target in the 'KxSMBSample' project\n//\n\n#import <Availabil"
  },
  {
    "path": "KxSMBSample/SmbAuthViewController.h",
    "chars": 2041,
    "preview": "//\n//  SmbAuthViewController.h\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 29.0"
  },
  {
    "path": "KxSMBSample/SmbAuthViewController.m",
    "chars": 8476,
    "preview": "//\n//  SmbAuthViewController.m\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 29.0"
  },
  {
    "path": "KxSMBSample/TreeViewController.h",
    "chars": 1751,
    "preview": "//\n//  TreeViewController.h\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 27.03.1"
  },
  {
    "path": "KxSMBSample/TreeViewController.m",
    "chars": 13537,
    "preview": "//\n//  TreeViewController.m\n//  kxsmb project\n//  https://github.com/kolyvan/kxsmb/\n//\n//  Created by Kolyvan on 27.03.1"
  },
  {
    "path": "KxSMBSample/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "KxSMBSample/main.m",
    "chars": 344,
    "preview": "//\n//  main.m\n//  KxSMBSample\n//\n//  Created by Kolyvan on 30.03.13.\n//  Copyright (c) 2013 Konstantin Bukreev. All righ"
  },
  {
    "path": "KxSMBSample.xcodeproj/project.pbxproj",
    "chars": 24191,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "LICENSE",
    "chars": 1298,
    "preview": "Copyright (c) 2013 Konstantin Bukreev. All rights reserved.\n\nRedistribution and use in source and binary forms, with or "
  },
  {
    "path": "Rakefile",
    "chars": 9696,
    "preview": "# \n# Rakefile\n# kxsmb project\n# https://github.com/kolyvan/kxsmb/\n#\n# Created by Kolyvan on 29.03.13.\n#\n\n#\n# Copyright ("
  },
  {
    "path": "readme.md",
    "chars": 1265,
    "preview": "KxSMB is objective-c wrapper for libsmbclient lib. \n===========================================\n\nFor now KxSMB supports "
  }
]

About this extraction

This page contains the full source code of the kolyvan/kxsmb GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (174.6 KB), approximately 43.7k tokens, and a symbol index with 2 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!