nplexity / xmpp-file-transfer-demo

iOS demo application using XMPPFramework for file transfer.
43 stars 24 forks source link

unable to send file transfer even if disableSocks5 is YES #11

Open madhukiranraju opened 9 years ago

madhukiranraju commented 9 years ago

Hi Jonathon Staff,

i have been trying to send file by disabling SockS5.

below is the code for button action

- (IBAction)btnTransferClicked:(id)sender
{
  if (!_fileTransfer) {
    _fileTransfer = [[XMPPOutgoingFileTransfer alloc]
                                               initWithDispatchQueue:dispatch_get_main_queue()];
    [_fileTransfer activate:[self appDelegate].xmppStream];
      _fileTransfer.disableSOCKS5 = YES;

    [_fileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];

  }
    UIImage *imagePic = [UIImage imageNamed:@"defaultPerson.png"];
    NSData *dataPic =  UIImagePNGRepresentation(imagePic);

  NSError *err;
  if (![_fileTransfer sendData:dataPic
                         named:@"defaultPerson.png"
                   toRecipient:[XMPPJID jidWithString:@"venu@xx.in/Smack"]
                   description:@"Baal's Soulstone, obviously."
                         error:&err]) {
    DDLogInfo(@"You messed something up: %@", err);
  }
}

and below is the console logs

2014-12-04 19:42:25:109 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: didActivate
2014-12-04 19:42:25:146 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: startFileTransfer:
2014-12-04 19:42:25:147 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: queryRecipientDiscoInfo
2014-12-04 19:42:25:147 FileTransferDemo[9400:1903] SEND: <iq type="get" to="venu@fxx.in/Smack" id="34C9AC7F-C733-45FD-A4FF-CD9A34BB55BA"><query xmlns="http://jabber.org/protocol/disco#info"/></iq>
2014-12-04 19:43:25:148 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: handleRecipientDiscoInfoQueryIQ:withInfo:
2014-12-04 19:43:25:149 FileTransferDemo[9400:807] iq: (null), info: <XMPPBasicTrackingInfo: 0x174270700>
2014-12-04 19:43:25:150 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: failWithReason:error:
2014-12-04 19:43:25:150 FileTransferDemo[9400:807] Outgoing file transfer failed because: Timeout waiting for recipient `disco#info` response.
2014-12-04 19:43:25:150 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: cleanUp
2014-12-04 19:43:25:151 FileTransferDemo[9400:807] XMPPOutgoingFileTransfer: Unable to send SI offer; the recipient doesn't have the required features.
2014-12-04 19:43:25:152 FileTransferDemo[9400:807] Outgoing file transfer failed with error: Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Timeout waiting for recipient `disco#info` response." UserInfo=0x170265fc0 {NSLocalizedDescription=Timeout waiting for recipient `disco#info` response.}
2014-12-04 19:43:25:225 FileTransferDemo[9400:807] Outgoing file transfer failed with error: Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x174078540 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.}

Please help me .

jonstaff commented 9 years ago

What device (I assume iOS or OS X) are you sending from and what are you sending to? It looks like you're using Smack; does this mean an Android device is receiving?

The error you're receiving is caused by a timeout (Outgoing file transfer failed because: Timeout waiting for recipientdisco#inforesponse.), indicating that the recipient doesn't properly handle an incoming request for disco#info.

huadee commented 9 years ago

yes,It works fine ,just set

xmppIncomingFileTransfer.disableIBB = NO;
xmppIncomingFileTransfer.disableSOCKS5 = YES;
gabber3000 commented 9 years ago

Sorry I can not understand. I managed successfully from iphone client to spark. Unfortunately I can not send files from iphone to iphone. here is the error:

Error Domain = XMPPOutgoingFileTransferErrorDomain Code = -1 "Unable to send SI offer; the recipient does not have the required features." UserInfo = {0x8388b8e0 NSLocalizedDescription = Unable to send SI offer; the recipient does not have the required features.}

gabber3000 commented 9 years ago

This and my Delegate class. As you can see I have included the code for file transfer. I think I have taken all the steps correctly. Unfortunately, the file transfer will not work. What am I doing wrong? thank you.

#import "AppDelegate.h"
#import "RootViewController.h"
#import "SettingsViewController.h"

#import "GCDAsyncSocket.h"
#import "XMPP.h"
#import "XMPPLogging.h"
#import "XMPPReconnect.h"
#import "XMPPCapabilitiesCoreDataStorage.h"
#import "XMPPRosterCoreDataStorage.h"
#import "XMPPvCardAvatarModule.h"
#import "XMPPvCardCoreDataStorage.h"

#import "DDLog.h"
#import "DDTTYLogger.h"

#import <CFNetwork/CFNetwork.h>
#import "SingletonDati.h"

// Log levels: off, error, warn, info, verbose
#if DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_INFO;
#endif

@interface AppDelegate(){

    XMPPIncomingFileTransfer *_xmppIncomingFileTransfer;
}

- (void)setupStream;
- (void)teardownStream;

- (void)goOnline;
- (void)goOffline;

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@implementation AppDelegate

@synthesize xmppStream;
@synthesize xmppReconnect;
@synthesize xmppRoster;
@synthesize xmppRosterStorage;
@synthesize xmppvCardTempModule;
@synthesize xmppvCardAvatarModule;
@synthesize xmppCapabilities;
@synthesize xmppCapabilitiesStorage;

@synthesize window;
@synthesize navigationController;
@synthesize settingsViewController;
@synthesize loginButton;
@synthesize password;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Configure logging framework

    [DDLog addLogger:[DDTTYLogger sharedInstance]
        withLogLevel:XMPP_LOG_LEVEL_VERBOSE | XMPP_LOG_FLAG_TRACE | XMPP_LOG_FLAG_SEND_RECV];

   /// [DDLog addLogger:[DDTTYLogger sharedInstance] withLogLevel:XMPP_LOG_FLAG_SEND_RECV];

    // Setup the XMPP stream

    [self setupStream];

    // Setup the view controllers

   /// [window setRootViewController:navigationController];
    [window makeKeyAndVisible];

    if (![self connect])
    {
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.0 * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

            [navigationController presentViewController:settingsViewController animated:YES completion:NULL];
        });
    }

    return YES;
}

- (void)dealloc
{
    [self teardownStream];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Core Data
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSManagedObjectContext *)managedObjectContext_roster
{
    return [xmppRosterStorage mainThreadManagedObjectContext];
}

- (NSManagedObjectContext *)managedObjectContext_capabilities
{
    return [xmppCapabilitiesStorage mainThreadManagedObjectContext];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Private
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)setupStream
{
    NSAssert(xmppStream == nil, @"Method setupStream invoked multiple times");

    // Setup xmpp stream
    //
    // The XMPPStream is the base class for all activity.
    // Everything else plugs into the xmppStream, such as modules/extensions and delegates.

    xmppStream = [[XMPPStream alloc] init];

#if !TARGET_IPHONE_SIMULATOR
    {
        // Want xmpp to run in the background?
        //
        // P.S. - The simulator doesn't support backgrounding yet.
        //        When you try to set the associated property on the simulator, it simply fails.
        //        And when you background an app on the simulator,
        //        it just queues network traffic til the app is foregrounded again.
        //        We are patiently waiting for a fix from Apple.
        //        If you do enableBackgroundingOnSocket on the simulator,
        //        you will simply see an error message from the xmpp stack when it fails to set the property.

        xmppStream.enableBackgroundingOnSocket = YES;
    }
#endif

    // Setup reconnect
    //
    // The XMPPReconnect module monitors for "accidental disconnections" and
    // automatically reconnects the stream for you.
    // There's a bunch more information in the XMPPReconnect header file.

    xmppReconnect = [[XMPPReconnect alloc] init];

    // Setup roster
    //
    // The XMPPRoster handles the xmpp protocol stuff related to the roster.
    // The storage for the roster is abstracted.
    // So you can use any storage mechanism you want.
    // You can store it all in memory, or use core data and store it on disk, or use core data with an in-memory store,
    // or setup your own using raw SQLite, or create your own storage mechanism.
    // You can do it however you like! It's your application.
    // But you do need to provide the roster with some storage facility.

  ///  xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
    xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] initWithInMemoryStore];

    xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage];

    xmppRoster.autoFetchRoster = YES;
    xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;

    // Setup vCard support
    //
    // The vCard Avatar module works in conjuction with the standard vCard Temp module to download user avatars.
    // The XMPPRoster will automatically integrate with XMPPvCardAvatarModule to cache roster photos in the roster.

    xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
    xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:xmppvCardStorage];

    xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:xmppvCardTempModule];

    // Setup capabilities
    //
    // The XMPPCapabilities module handles all the complex hashing of the caps protocol (XEP-0115).
    // Basically, when other clients broadcast their presence on the network
    // they include information about what capabilities their client supports (audio, video, file transfer, etc).
    // But as you can imagine, this list starts to get pretty big.
    // This is where the hashing stuff comes into play.
    // Most people running the same version of the same client are going to have the same list of capabilities.
    // So the protocol defines a standardized way to hash the list of capabilities.
    // Clients then broadcast the tiny hash instead of the big list.
    // The XMPPCapabilities protocol automatically handles figuring out what these hashes mean,
    // and also persistently storing the hashes so lookups aren't needed in the future.
    //
    // Similarly to the roster, the storage of the module is abstracted.
    // You are strongly encouraged to persist caps information across sessions.
    //
    // The XMPPCapabilitiesCoreDataStorage is an ideal solution.
    // It can also be shared amongst multiple streams to further reduce hash lookups.

    xmppCapabilitiesStorage = [XMPPCapabilitiesCoreDataStorage sharedInstance];
    xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:xmppCapabilitiesStorage];

    xmppCapabilities.autoFetchHashedCapabilities = NO;
    xmppCapabilities.autoFetchNonHashedCapabilities = NO;

    // Activate xmpp modules

    [xmppReconnect         activate:xmppStream];
    [xmppRoster            activate:xmppStream];
   //  [xmppvCardTempModule   activate:xmppStream];
  ///  [xmppvCardAvatarModule activate:xmppStream];
      [xmppCapabilities      activate:xmppStream];

    // Add ourself as a delegate to anything we may be interested in

    [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];

    _xmppIncomingFileTransfer = [XMPPIncomingFileTransfer new];
    [_xmppIncomingFileTransfer activate:xmppStream];
    // Add ourselves as delegate to necessary methods
    [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];

    // Optional:
    //
    // Replace me with the proper domain and port.
    // The example below is setup for a typical google talk account.
    //
    // If you don't supply a hostName, then it will be automatically resolved using the JID (below).
    // For example, if you supply a JID like 'user@quack.com/rsrc'
    // then the xmpp framework will follow the xmpp specification, and do a SRV lookup for quack.com.
    //
    // If you don't specify a hostPort, then the default (5222) will be used.

    [xmppStream setHostName:@""];
    [xmppStream setHostPort:5222];

    // You may need to alter these settings depending on the server you're connecting to
    customCertEvaluation = YES;
}

- (void)teardownStream
{
    [xmppStream removeDelegate:self];
    [xmppRoster removeDelegate:self];

    [xmppReconnect         deactivate];
    [xmppRoster            deactivate];
    [xmppvCardTempModule   deactivate];
    [xmppvCardAvatarModule deactivate];
    [xmppCapabilities      deactivate];

    [xmppStream disconnect];

    xmppStream = nil;
    xmppReconnect = nil;
    xmppRoster = nil;
    xmppRosterStorage = nil;
    xmppvCardStorage = nil;
    xmppvCardTempModule = nil;
    xmppvCardAvatarModule = nil;
    xmppCapabilities = nil;
    xmppCapabilitiesStorage = nil;
}

// It's easy to create XML elments to send and to read received XML elements.
// You have the entire NSXMLElement and NSXMLNode API's.
//
// In addition to this, the NSXMLElement+XMPP category provides some very handy methods for working with XMPP.
//
// On the iPhone, Apple chose not to include the full NSXML suite.
// No problem - we use the KissXML library as a drop in replacement.
//
// For more information on working with XML elements, see the Wiki article:
// https://github.com/robbiehanson/XMPPFramework/wiki/WorkingWithElements

- (void)goOnline
{
    XMPPPresence *presence = [XMPPPresence presence]; // type="available" is implicit

    NSString *domain = [xmppStream.myJID domain];

    //Google set their presence priority to 24, so we do the same to be compatible.

    if([domain isEqualToString:@"gmail.com"]
       || [domain isEqualToString:@"gtalk.com"]
       || [domain isEqualToString:@"talk.google.com"])
    {
        NSXMLElement *priority = [NSXMLElement elementWithName:@"priority" stringValue:@"24"];
        [presence addChild:priority];
    }

    [[self xmppStream] sendElement:presence];
}

- (void)goOffline
{
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];

    [[self xmppStream] sendElement:presence];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Connect/disconnect
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)connect
{
    if (![xmppStream isDisconnected]) {
        return YES;
    }

    NSString *myJID = [[NSUserDefaults standardUserDefaults] stringForKey:kXMPPmyJID];
    NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:kXMPPmyPassword];

    //
    // If you don't want to use the Settings view to set the JID,
    // uncomment the section below to hard code a JID and password.
    //
    // myJID = @"user@gmail.com/xmppframework";
    // myPassword = @"";

    if (myJID == nil || myPassword == nil) {
        return NO;
    }

    [xmppStream setMyJID:[XMPPJID jidWithString:myJID]];
    password = myPassword;

    NSError *error = nil;
    if (![xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error])
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error connecting"
                                                            message:@"See console for error details."
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];

        DDLogError(@"Error connecting: %@", error);

        return NO;
    }

    return YES;
}

- (void)disconnect
{
    [self goOffline];
    [xmppStream disconnect];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark UIApplicationDelegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store
    // enough application state information to restore your application to its current state in case
    // it is terminated later.
    //
    // If your application supports background execution,
    // called instead of applicationWillTerminate: when the user quits.

    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

#if TARGET_IPHONE_SIMULATOR
    DDLogError(@"The iPhone simulator does not process background network traffic. "
               @"Inbound traffic is queued until the keepAliveTimeout:handler: fires.");
#endif

    if ([application respondsToSelector:@selector(setKeepAliveTimeout:handler:)])
    {
        [application setKeepAliveTimeout:600 handler:^{

            DDLogVerbose(@"KeepAliveHandler");

            // Do other keep alive stuff here.
        }];
    }
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    [self teardownStream];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPStream Delegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    NSLog(@"*** connessione SOCKET RIUSCITA");
}

- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    NSString *expectedCertName = [xmppStream.myJID domain];
    if (expectedCertName)
    {
        [settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
    }

    if (customCertEvaluation)
    {
        [settings setObject:@(YES) forKey:GCDAsyncSocketManuallyEvaluateTrust];
    }
}

/**
 * Allows a delegate to hook into the TLS handshake and manually validate the peer it's connecting to.
 *
 * This is only called if the stream is secured with settings that include:
 * - GCDAsyncSocketManuallyEvaluateTrust == YES
 * That is, if a delegate implements xmppStream:willSecureWithSettings:, and plugs in that key/value pair.
 *
 * Thus this delegate method is forwarding the TLS evaluation callback from the underlying GCDAsyncSocket.
 *
 * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer.
 *
 * Note from Apple's documentation:
 *   Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain,
 *   [it] might block while attempting network access. You should never call it from your main thread;
 *   call it only from within a function running on a dispatch queue or on a separate thread.
 *
 * This is why this method uses a completionHandler block rather than a normal return value.
 * The idea is that you should be performing SecTrustEvaluate on a background thread.
 * The completionHandler block is thread-safe, and may be invoked from a background queue/thread.
 * It is safe to invoke the completionHandler block even if the socket has been closed.
 *
 * Keep in mind that you can do all kinds of cool stuff here.
 * For example:
 *
 * If your development server is using a self-signed certificate,
 * then you could embed info about the self-signed cert within your app, and use this callback to ensure that
 * you're actually connecting to the expected dev server.
 *
 * Also, you could present certificates that don't pass SecTrustEvaluate to the client.
 * That is, if SecTrustEvaluate comes back with problems, you could invoke the completionHandler with NO,
 * and then ask the client if the cert can be trusted. This is similar to how most browsers act.
 *
 * Generally, only one delegate should implement this method.
 * However, if multiple delegates implement this method, then the first to invoke the completionHandler "wins".
 * And subsequent invocations of the completionHandler are ignored.
 **/
- (void)xmppStream:(XMPPStream *)sender didReceiveTrust:(SecTrustRef)trust
 completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    // The delegate method should likely have code similar to this,
    // but will presumably perform some extra security code stuff.
    // For example, allowing a specific self-signed certificate that is known to the app.

    dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(bgQueue, ^{

        SecTrustResultType result = kSecTrustResultDeny;
        OSStatus status = SecTrustEvaluate(trust, &result);

        if (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) {
            completionHandler(YES);
        }
        else {
            completionHandler(NO);
        }
    });
}

- (void)xmppStreamDidSecure:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    isXmppConnected = YES;

    NSError *error = nil;

    if (![[self xmppStream] authenticateWithPassword:password error:&error])
    {
        DDLogError(@"Error authenticating: %@", error);
    }
}

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    [self goOnline];
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

        return NO;
}

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    // A simple example of inbound message handling.

    if ([message isChatMessageWithBody])
    {
        XMPPUserCoreDataStorageObject *user = [xmppRosterStorage userForJID:[message from]
                                                                 xmppStream:xmppStream
                                                       managedObjectContext:[self managedObjectContext_roster]];

        NSString *body = [[message elementForName:@"body"] stringValue];
        NSString *displayName = [user displayName];

        if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
        {
          /*  UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:displayName
                                                                message:body
                                                               delegate:nil
                                                      cancelButtonTitle:@"Ok"
                                                      otherButtonTitles:nil];
            [alertView show];
           */

            NSMutableDictionary *dati=[[NSMutableDictionary alloc]init];
            [dati setObject:(body) forKey:@"messaggioRicevuto"];
            [dati setObject:displayName forKey:@"remoteUser"];

            [[NSNotificationCenter defaultCenter] postNotificationName:@"RiceviMessaggio" object:self userInfo:dati];

        }
        else
        {
            // We are not active, so use a local notification instead
            UILocalNotification *localNotification = [[UILocalNotification alloc] init];
            localNotification.alertAction = @"Ok";
            localNotification.alertBody = [NSString stringWithFormat:@"From: %@\n\n%@",displayName,body];

            [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
        }
    }
}

- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
    DDLogVerbose(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, [presence fromStr]);

}

- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    if (!isXmppConnected)
    {
        DDLogError(@"Unable to connect to server. Check xmppStream.hostName");
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPRosterDelegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)xmppRoster:(XMPPRoster *)sender didReceiveBuddyRequest:(XMPPPresence *)presence
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    XMPPUserCoreDataStorageObject *user = [xmppRosterStorage userForJID:[presence from]
                                                             xmppStream:xmppStream
                                                   managedObjectContext:[self managedObjectContext_roster]];

    NSString *displayName = [user displayName];
    NSString *jidStrBare = [presence fromStr];
    NSString *body = nil;

    if (![displayName isEqualToString:jidStrBare])
    {
        body = [NSString stringWithFormat:@"Buddy request from %@ <%@>", displayName, jidStrBare];
    }
    else
    {
        body = [NSString stringWithFormat:@"Buddy request from %@", displayName];
    }

    if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:displayName
                                                            message:body 
                                                           delegate:nil 
                                                  cancelButtonTitle:@"Not implemented"
                                                  otherButtonTitles:nil];
        [alertView show];
    } 
    else 
    {
        // We are not active, so use a local notification instead
        UILocalNotification *localNotification = [[UILocalNotification alloc] init];
        localNotification.alertAction = @"Not implemented";
        localNotification.alertBody = body;

        [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
    }

}

//IN ARRIVO
#pragma mark - XMPPIncomingFileTransferDelegate Methods

- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
                didFailWithError:(NSError *)error
{
    DDLogVerbose(@"%@: Incoming file transfer failed with error: %@", THIS_FILE, error);
}

- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
               didReceiveSIOffer:(XMPPIQ *)offer
{
    DDLogVerbose(@"%@: Incoming file transfer did receive SI offer. Accepting...", THIS_FILE);
    [sender acceptSIOffer:offer];
}

- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
              didSucceedWithData:(NSData *)data
                           named:(NSString *)name
{
    DDLogVerbose(@"%@: Incoming file transfer did succeed.", THIS_FILE);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:name];
    [data writeToFile:fullPath options:0 error:nil];

    DDLogVerbose(@"%@: Data was written to the path: %@", THIS_FILE, fullPath);
}

////test--------------

///INVIO
- (void)xmppOutgoingFileTransfer:(XMPPOutgoingFileTransfer *)sender
                didFailWithError:(NSError *)error
{
    DDLogInfo(@"Outgoing file transfer failed with error: %@", error);

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                                    message:@"There was an error sending your file. See the logs."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (void)xmppOutgoingFileTransferDidSucceed:(XMPPOutgoingFileTransfer *)sender
{
    DDLogVerbose(@"File transfer successful.");

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
                                                    message:@"Your file was sent successfully."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

#pragma mark - Public Methods

- (void)prepareStreamAndLogInWithJID:(XMPPJID *)jid password:(NSString *)password
{
    DDLogVerbose(@"Preparing the stream and logging in as %@", jid.full);

    xmppStream = [XMPPStream new];
    xmppStream.myJID = jid;

    xmppRosterStorage = [XMPPRosterCoreDataStorage new];
    xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage];
    xmppRoster.autoFetchRoster = YES;

    _xmppIncomingFileTransfer = [XMPPIncomingFileTransfer new];

    // Activate all modules
    [xmppRoster activate:xmppStream];
    [_xmppIncomingFileTransfer activate:xmppStream];

    // Add ourselves as delegate to necessary methods
    [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];

    NSError *err;
    if (![xmppStream connectWithTimeout:30 error:&err]) {
        DDLogInfo(@"%@: Error connecting: %@", THIS_FILE, err);
    } else {
        password = password;
    }
}

#pragma mark - Private Methods

- (void)tearDownStream
{
    [xmppStream removeDelegate:self];
    [_xmppIncomingFileTransfer removeDelegate:self];

    [xmppRoster deactivate];
    [_xmppIncomingFileTransfer deactivate];

    [xmppStream disconnect];

    xmppStream = nil;
    xmppRoster = nil;
    xmppRosterStorage = nil;
    _xmppIncomingFileTransfer = nil;
}

#pragma mark - XMPPStreamDelegate Methods

@end
venkateswarlunp commented 9 years ago

I do got same error info:

Outgoing file transfer failed with error: Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x7fc960ce3a80 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.}

Hope... one of us will update with 'Solution' ASAP.

gabber3000 commented 9 years ago

:( Are five days that I'm going crazy to fix this error with no results. I really hope that some user can give a solution. Thank You.

jonstaff commented 9 years ago

What are the JIDs for each device you're sending to and receiving from?

venkateswarlunp commented 9 years ago

Hello Jonstaff,

Sender JID : Sender-username@domain/resource Receiver JID : Receiver-username@domain/resource

Resource : taken from full JID in presence delegate

e.g: test123@192.165.4.78/237956891081628672871

Thanks in advance

jonstaff commented 9 years ago

I need the actual JIDs you're using in the failed transfer, not a description of how they work.

venkateswarlunp commented 9 years ago

Sender JID: 2000530038@192.168.XX.XX/4184529043698432067

Receiver JID: 918790013636@192.168.XX.XX/23749801272032817639

Where , 192.168.XX.XX is local host

gabber3000 commented 9 years ago

these are my logs:

my iphone send file to client ( iOS simulator)

2015-02-09 19:40:48.391 SecretChat[23833:3009098] ******test@185.58.111.111/test
2015-02-09 19:40:50:499 SecretChat[23833:9e0b] SEND: <iq type="get" to="test@185.58.111.111/test" id="511EF964-A679-456E-A4FD-70F51D104F1A"><query xmlns="http://jabber.org/protocol/disco#info"/></iq>
2015-02-09 19:40:50:605 SecretChat[23833:9e0b] RECV: <iq xmlns="jabber:client" type="result" to="admin@185.58.111.111/test" id="511EF964-A679-456E-A4FD-70F51D104F1A" from="test@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"><feature var="http://jabber.org/protocol/disco#info"/><feature var="http://jabber.org/protocol/caps"/></query></iq>
2015-02-09 19:40:50:609 SecretChat[23833:1903] SEND: <iq type="result" to="test@185.58.111.111/test" id="511EF964-A679-456E-A4FD-70F51D104F1A" from="admin@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"><identity category="client" type="ios-osx"/><feature var="http://jabber.org/protocol/si"/><feature var="http://jabber.org/protocol/si/profile/file-transfer"/><feature var="http://jabber.org/protocol/bytestreams"/><feature var="http://jabber.org/protocol/ibb"/></query></iq>
2015-02-09 19:40:50:650 SecretChat[23833:807] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:40:50:650 SecretChat[23833:807] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:40:51:702 SecretChat[23833:9e0b] RECV: <iq xmlns="jabber:client" type="result" to="admin@185.58.111.111/test" id="511EF964-A679-456E-A4FD-70F51D104F1A" from="test@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"><identity category="client" type="ios-osx"/><feature var="http://jabber.org/protocol/si"/><feature var="http://jabber.org/protocol/si/profile/file-transfer"/><feature var="http://jabber.org/protocol/bytestreams"/><feature var="http://jabber.org/protocol/ibb"/></query></iq>
2015-02-09 19:40:52:852 SecretChat[23833:807] Outgoing file transfer failed with error: Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x18b3b110 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.}
2015-02-09 19:40:52:853 SecretChat[23833:807] Error performing fetch: Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x18b3b110 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.}
2015-02-09 19:40:52:866 SecretChat[23833:9e0b] XMPPCapabilities: Hash mismatch! hash(VyOFcFX6+YNmKssVXSBKGFP0BS4=) != calculatedHash(ITcwJgVrZLkXrP0EGRyR1B+pHIY=)
2015-02-09 19:40:52:868 SecretChat[23833:9e0b] XMPPCapabilities: maybeQueryNextJidWithHashKey:dueToHashMismatch: - Key doesn't exist in discoRequestHashDict
2015-02-09 19:40:53:110 SecretChat[23833:807] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:40:53:110 SecretChat[23833:807] AppDelegate: xmppStream:didReceiveIQ

IOS (SIMULATOR RECEIVING)

2015-02-09 19:47:55:541 SecretChat[3238:7207] RECV: <iq xmlns="jabber:client" type="get" to="test@185.58.111.111/test" id="05F88A04-4157-4A9C-8633-C8E68137E4EC" from="admin@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"/></iq>
2015-02-09 19:47:55:541 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:47:55:541 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:47:55:541 SecretChat[3238:2c0b] SEND: <iq type="result" to="admin@185.58.111.111/test" id="05F88A04-4157-4A9C-8633-C8E68137E4EC"><query xmlns="http://jabber.org/protocol/disco#info"><feature var="http://jabber.org/protocol/disco#info"/><feature var="http://jabber.org/protocol/caps"/></query></iq>
2015-02-09 19:47:55:542 SecretChat[3238:2c0b] SEND: <iq type="result" to="admin@185.58.111.111/test" id="05F88A04-4157-4A9C-8633-C8E68137E4EC" from="test@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"><identity category="client" type="ios-osx"/><feature var="http://jabber.org/protocol/si"/><feature var="http://jabber.org/protocol/si/profile/file-transfer"/><feature var="http://jabber.org/protocol/bytestreams"/><feature var="http://jabber.org/protocol/ibb"/></query></iq>
2015-02-09 19:47:55:746 SecretChat[3238:2c0b] RECV: <iq xmlns="jabber:client" type="result" to="test@185.58.111.111/test" id="05F88A04-4157-4A9C-8633-C8E68137E4EC" from="admin@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"><identity category="client" type="ios-osx"/><feature var="http://jabber.org/protocol/si"/><feature var="http://jabber.org/protocol/si/profile/file-transfer"/><feature var="http://jabber.org/protocol/bytestreams"/><feature var="http://jabber.org/protocol/ibb"/></query></iq>
2015-02-09 19:47:55:746 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:47:55:747 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:47:55:748 SecretChat[3238:7207] XMPPCapabilities: Hash mismatch! hash(VyOFcFX6+YNmKssVXSBKGFP0BS4=) != calculatedHash(ITcwJgVrZLkXrP0EGRyR1B+pHIY=)
2015-02-09 19:47:55:748 SecretChat[3238:7207] XMPPCapabilities: maybeQueryNextJidWithHashKey:dueToHashMismatch: - Key doesn't exist in discoRequestHashDict
2015-02-09 19:48:03:047 SecretChat[3238:7207] RECV: <iq xmlns="jabber:client" type="get" to="test@185.58.111.111/test" id="9D9951FB-F319-4698-BEA2-32AED6CDFC04" from="admin@185.58.111.111/test"><query xmlns="http://jabber.org/protocol/disco#info"/></iq>
2015-02-09 19:48:03:047 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:48:03:047 SecretChat[3238:607] AppDelegate: xmppStream:didReceiveIQ:
2015-02-09 19:48:03:048 SecretChat[3238:7207] SEND: <iq type="result" to="admin@185.58.111.111/test" id="9D9951FB-F319-4698-BEA2-32AED6CDFC04"><query xmlns="http://jabber.org/protocol/disco#info"><feature var="http://jabber.org/protocol/disco#info"/><feature var="http://jabber.org/protocol/caps"/></query></iq>
gabber3000 commented 9 years ago

I wanted to inform you guys that I have succeeded. After 10 days of work I have reached this conclusion. I deleted my class AppDelegate taken from the sample project framework xmmpframework. Right now I'm working with the class AppDelegate Project File-Transfer-Demo and everything works. I really hope that in the next release of the framework XMPPFRAMEWORK in the sample project for IOS will also include File-Transfer-Demo. Thanks for the support.

venkateswarlunp commented 9 years ago

That's great :)

GitHubxdw commented 9 years ago

你好jonstaff,我在传输文件中一直返回失败 ,麻烦你帮我看下是什么情况,多谢。 这是我的代码:

XMPPJID *jid = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@/Smack",recipient,ServerName]];
if (!_fileTransfer) {

    _fileTransfer = [[XMPPOutgoingFileTransfer alloc]
                     initWithDispatchQueue:dispatch_get_main_queue()];
    _fileTransfer.disableSOCKS5 = YES;
    [_fileTransfer activate:_xmppStream];
    [_fileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
NSError *err;
if (![_fileTransfer sendData:fileData
                       named:fileName
                 toRecipient:jid
                 description:description
                       error:&err]) {
    //不能传输
    NSLog(@"不能传输");
}

返回的错误信息是: Error Domain = XMPPOutgoingFileTransferErrorDomain Code = -1 "Unable to send SI offer

jonstaff commented 9 years ago

你好。我的中文不好,所以请原谅我。在哪里说 Error Domain = XMPPOutgoingFileTransferErrorDomain Code = -1 "Unable to send SI offer ? 还有什么说?这个错误不是完整。

GitHubxdw commented 9 years ago

你好jonstaff,很高兴收到你的回复,我的完整错误信息是:Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x15e59910 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.} 其中,我要传输到的Jid信息是13430420870@win-qlsnkq93bfg/Smack。再次感谢你的回复,希望能和你有更多的交流,我来自中国深圳。

aelbarji commented 9 years ago

Hello Everybody, since two weeks i'm facing this problem when i trying to send files. Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features." UserInfo=0x15e59910 {NSLocalizedDescription=Unable to send SI offer; the recipient doesn't have the required features.} Does anyone find how to fix this error ? I need some help please...

dushmantha commented 9 years ago

copied by venkateswarlunp. it work for me. Sender JID : Sender-username@domain/resource Receiver JID : Receiver-username@domain/resource

Resource : taken from full JID in presence delegate

zinnuree commented 9 years ago

I got it working this way-

  1. Inside setupStrem method, set up the incoming end like this -
xmppIncomingFileTransfer = [[XMPPIncomingFileTransfer alloc] init];
xmppIncomingFileTransfer.disableIBB = NO;
xmppIncomingFileTransfer.disableSOCKS5 = NO;

[xmppIncomingFileTransfer   activate:xmppStream];

[xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
  1. Implement the incoming end delegate methods-
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
                didFailWithError:(NSError *)error
{
    DDLogVerbose(@"%@: Incoming file transfer failed with error: %@", THIS_FILE, error);
}

- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
               didReceiveSIOffer:(XMPPIQ *)offer
{
    DDLogVerbose(@"%@: Incoming file transfer did receive SI offer. Accepting...", THIS_FILE);
    [sender acceptSIOffer:offer];
}

- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
              didSucceedWithData:(NSData *)data
                           named:(NSString *)name
{
    DDLogVerbose(@"%@: Incoming file transfer did succeed.", THIS_FILE);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:name];
    [data writeToFile:fullPath options:0 error:nil];

    DDLogVerbose(@"%@: Data was written to the path: %@", THIS_FILE, fullPath);
}

Incoming files will be written to the documents directory, you can update UI when it is done.

  1. On the sending side-
if (!_fileTransfer) {
        _fileTransfer = [[XMPPOutgoingFileTransfer alloc] initWithDispatchQueue:dispatch_get_main_queue()];

        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        [_fileTransfer activate:appDelegate.xmppStream];
        _fileTransfer.disableIBB = NO;
        _fileTransfer.disableSOCKS5 = NO;

        [_fileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
    }

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:filename];
    NSData *data = [NSData dataWithContentsOfFile:fullPath];

    NSError *err;
    if (![_fileTransfer sendData:data
                           named:filename
                     toRecipient:[XMPPJID jidWithString:self.contact.primaryResource.jidStr]
                     description:@"Baal's Soulstone, obviously."
                           error:&err]) {
        DDLogInfo(@"You messed something up: %@", err);
    }
  1. Implement the outgoing delegate methods -
- (void)xmppOutgoingFileTransfer:(XMPPOutgoingFileTransfer *)sender
                didFailWithError:(NSError *)error
{
    DDLogInfo(@"Outgoing file transfer failed with error: %@", error);

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                                    message:@"There was an error sending your file. See the logs."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (void)xmppOutgoingFileTransferDidSucceed:(XMPPOutgoingFileTransfer *)sender
{
    DDLogVerbose(@"File transfer successful.");

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
                                                    message:@"Your file was sent successfully."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

Note that the disableIBB and disableSOCKS5 should match at both ends. If the problem still exists, go to XMPPOutgoingFileTransfer.m and then to the method-

Then put a NSLOG/Breakpoint at this line -

hasSOCKS5 = hasSI && hasFT && hasSOCKS5;
hasIBB = hasSI && hasFT && hasIBB;

Both values should become TRUE when sending a file. Check which one is FALSE (causing the error), you will get an idea why the incoming end is sending FALSE instantly. Try to fix that.

lionsom commented 8 years ago

the sender in the wifi , the receive in 4G send file failed. it's the outcoming’s log and the receive's log below.

2015-12-22 19:22:32.468 pirate[789:225573] XMPPOutgoingFileTransfer:startFileTransfer 2015-12-22 19:22:32:471 pirate[789:e6b] 4-SEND: Baal's Soulstone, obviously.http://jabber.org/protocol/bytestreamshttp://jabber.org/protocol/ibb 2015-12-22 19:22:32:765 pirate[789:e6b] 3-RECV: http://jabber.org/protocol/bytestreams 2015-12-22 19:22:32:772 pirate[789:4f3f] 4-SEND: 2015-12-22 19:22:32:777 pirate[789:4f3f] 3-RECV: 2015-12-22 19:22:32:782 pirate[789:5a43] 4-SEND: 2015-12-22 19:23:32.784 pirate[789:225573] XMPPOutgoingFileTransfer:failWithReason 2015-12-22 19:23:32:786 pirate[789:b07] Outgoing file transfer failed with error: Error Domain=GCDAsyncSocketErrorDomain Code=2 "Invalid host parameter (nil or ""). Should be a domain name or IP address string." UserInfo={NSLocalizedDescription=Invalid host parameter (nil or ""). Should be a domain name or IP address string.}

the incoming’s log:

2015-12-22 19:26:22:377 pirate[789:5a67] 3-RECV: 2015-12-22 19:26:22:381 pirate[789:4937] 4-SEND: 2015-12-22 19:26:22:606 pirate[789:4937] 3-RECV: Baal's Soulstone, obviously.http://jabber.org/protocol/bytestreamshttp://jabber.org/protocol/ibb 2015-12-22 19:26:22:613 pirate[789:e73] 4-SEND: http://jabber.org/protocol/bytestreams 2015-12-22 19:26:23:629 pirate[789:5a67] 3-RECV:

my server: mod_proxy65: host: “proxy.auto.hooca.com.cn" name: ”file transfer proxy” ip: “192.168.0.25” hostname: “140.206.XX.XX” port: 7777 shaper: proxy_shaper

nitinios commented 8 years ago

Hi Please have a look i am getting this error

i am completely new in xmpp and openfire both so please tell me why i am getting this error jid i am supplying are correct i read all comments above unable to get solution

i am using file transfer demo. The lines below i just copied from above i want to know how to make this congfiguration on openfire where do if can find these variables.

my server: mod_proxy65: host: “proxy.auto.hooca.com.cn" name: ”file transfer proxy” ip: “192.168.0.25” hostname: “140.206.XX.XX” port: 7777 shaper: proxy_shaper

Thanks a lot

vishaldeshai commented 8 years ago

Is it possible to transfer offline file with above code.

above code is working for sending file when users are online.

I need to transfer when recipient is offline and once recipient is online, file should be fetch.

thanks in advance.