1
votes
    file_upload.setOnClickListener {
                // Create the file transfer manager
                var fileTransferInstance:FileTransferManager=FileTransferManager.getInstanceFor(connection)
                Log.i("id is ",contactPersonJId)
                // Create the outgoing file transfer
                fileTransderReceiver=fileTransferInstance.createOutgoingFileTransfer(contactPersonJId+"/Smack")

                //location of the file
                locateFile()

            }

    fun locateFile(){
            var intent:Intent= Intent(applicationContext,LocateImage::class.java )
           startActivityForResult(intent,IMAGE_REQUEST)
        }


     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)

            when(requestCode){

                IMAGE_REQUEST -> {

                    when(resultCode){

                        Activity.RESULT_OK -> {

                            val selectedImagePath=data?.getSerializableExtra(LocateImage.Obj.MEDIA_DATA) as? String
                            Log.v("file path is ",selectedImagePath)

                            if(!selectedImagePath.isNullOrEmpty()){

                                fileTransderReceiver?.sendFile(File(selectedImagePath),"First file")

                                                      GetFileResponse().execute(fileTransderReceiver)     


                        }
                    }
                }
            }

        }

 private inner class GetFileResponse : AsyncTask<OutgoingFileTransfer?, String,fileTransfer>() {


        var progressDialog: ProgressDialog? = null

        override fun onPreExecute() {
            progressDialog = ProgressDialog.show(this@chatActivity,
                    null, "Sending File...");
        }


        override fun doInBackground(vararg params: OutgoingFileTransfer?): fileTransfer {

            while (!params[0]?.isDone!!){

                if (params[0]?.status!!.equals(FileTransfer.Status.error)){
                    println("ERROR!!! " + params[0]?.error);
                }else{
                    println(params[0]?.status);
                    println(params[0]?.progress);
                }
            }

            var fileTransferState=fileTransfer("abc")
            fileTransferState.status=params[0]?.status
            fileTransferState.exception=params[0]?.exception
            fileTransferState.error=params[0]?.error

            return fileTransferState

        }

        override fun onPostExecute(result: fileTransfer?) {
            if(progressDialog!!.isShowing)
                progressDialog!!.dismiss()

            Log.v("Status is ",result?.status.toString())
            Log.v("Exception is ",result?.exception.toString())
            Log.v("Error is ",result?.error.toString())
        }

    }

Logcat:

05-05 18:03:34.593 30459-30459/com.example.itstym.chat_3 V/Status is: Error
05-05 18:03:34.593 30459-30459/com.example.itstym.chat_3 V/Exception is: org.jivesoftware.smack.XMPPException$XMPPErrorException: XMPPError: service-unavailable - cancel
05-05 18:03:34.593 30459-30459/com.example.itstym.chat_3 V/Error is: null

I have also check on the ejabberd server that file sending/receiving service is available. Even the progress is not also not updating from 0 but the status is changed from initial to negotiating transfer.

Update:

Sent XML

http://jabber.org/protocol/si' id='jsi_8960344439394443510' mime-type='image/jpeg' profile='http://jabber.org/protocol/si/profile/file-transfer'>First filehttp://jabber.org/protocol/bytestreamshttp://jabber.org/protocol/ibb

Receive XML

 <iq xml:lang='en' to='[email protected]/Android' from='[email protected]/Smack' type='error' id='0GfHL-35'><si xmlns='http://jabber.org/protocol/si' id='jsi_8960344439394443510' mime-type='image/jpeg' profile='http://jabber.org/protocol/si/profile/file-transfer'><file xmlns='http://jabber.org/protocol/si/profile/file-transfer' name='attachment.jpg'><desc>First file</desc></file><feature xmlns='http://jabber.org/protocol/feature-neg'><x xmlns='jabber:x:data' type='form'><field var='stream-method' type='list-single'><option><value>http://jabber.org/protocol/bytestreams</value></option><option><value>http://jabber.org/protocol/ibb</value></option></field></x></feature></si><error code='503' type='cancel'><service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/><text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>User session not found</text></error></iq>, 

It says error code 503 service unavailable.

[this link][1] say 503 error means you are not providing the full JId.

To get the full jId:

roster.getPresence(entry.user).from

it is also returning Jid in the form of [email protected]

Update 2:

using following code I checked weather file transfer service is availabe or not

var serviceDiscoveryManager:ServiceDiscoveryManager= ServiceDiscoveryManager.getInstanceFor(connection) serviceDiscoveryManager.addFeature("http://jabber.org/protocol/disco#info") serviceDiscoveryManager.addFeature("jabber:iq:privacy")

        Log.i("Service enable ",FileTransferNegotiator.isServiceEnabled(connection).toString())

But it is returning false that means file transfer service is unavailable.

How to enable file transfer service? FileTransferNegotiator. setServiceEnabled() function is not available.

1

1 Answers

1
votes

I had same problem then I investigated the stanza and solved it this way.

Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.

Resource path is changing with every presence changed of user. Lets say we want to send image to this user: "user1@mydomain"

You must add "/Resource" or "/Smack" part to this jid and it become this: user1@mydomain/Resource

But resource path is changing with presence so you must follow every presence change to update resource path. Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:

Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
                @Override
                public void entriesAdded(Collection<Jid> addresses) {
                    Log.d("entriesAdded", "ug");
                    context.sendBroadcast(new Intent("ENTRIES_ADDED"));
                }

                @Override
                public void entriesUpdated(Collection<Jid> addresses) {
                    Log.d("entriesUpdated", "ug");
                }

                @Override
                public void entriesDeleted(Collection<Jid> addresses) {
                    Log.d("entriesDeleted", "ug");
                }

                @Override
                public void presenceChanged(Presence presence) {
                    Log.d("presenceChanged", "ug");
                    //Resource from presence
                    String resource = presence.getFrom().getResourceOrEmpty().toString();
                    //Update resource part for user in DB or preferences
                    //...
                }
            });
}

Resource string will be some generated string like "6u1613j3kv" and jid will become: user1@mydomain/6u1613j3kv

That means that you must create your outgoing transfer like this:

EntityFullJid jid = JidCreate.entityFullFrom("user1@mydomain/6u1613j3kv"); 
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)

In your case you are using /Smack which is incorrect. Use resource part from presence.

And that is how i have solved my problem with file transfer on smack and Openfire.

Also to mention you must add following properties in your Openfire server:

xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port -7777

Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.

Also this can be configured the easy way, just set the resource on

XMPPTCPConnectionConfiguration.Builder .

like

XMPPTCPConnectionConfiguration.Builder configurationBuilder = 
XMPPTCPConnectionConfiguration.builder(); 

configurationBuilder.setResource("yourResourceName");