Now am working with Xmpp file Transfer in Android.While am sending files it give a 503 service unavailable error.
> 07-29 15:33:12.183: D/SMACK(22151): 03:33:12 PM SENT (1095973320):
> <iq id="s9DTB-10" to="jithu4#jabber.org/Smack"
> from="jithu1#jabber.org/Smack" type="set"><si
> xmlns="http://jabber.org/protocol/si" id="jsi_6388535575928662640"
> mime-type="image/jpeg"
> profile="http://jabber.org/protocol/si/profile/file-transfer"><file
> xmlns="http://jabber.org/protocol/si/profile/file-transfer"
> name="IMG_20130715_115622_1445552069.jpg" size="2105955"
> ><desc>test_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></iq>
After that it gives a response like that
> 07-29 15:33:12.708: D/SMACK(22151): 03:33:12 PM RCV (1095973320): <iq
> from='jithu4#jabber.org/Smack' to='jithu1#jabber.org/Smack'
> type='error' id='s9DTB-10'><error code='503'
> type='cancel'><service-unavailable
> xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>
Am using the asmack-android-6-0.8.1.1.jar libs
Please help me ,why this 503 error happens.
Add service discovery to your code. You can use below code for file transfer.
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(kXmppConnection);
if (sdm == null)
sdm = new ServiceDiscoveryManager(XmppConnection);
sdm.addFeature("http://jabber.org/protocol/disco#info");
sdm.addFeature("jabber:iq:privacy");
FileTransferNegotiator.setServiceEnabled(kXmppConnection,
true);
FileTransferManager manager = new FileTransferManager(
kXmppConnection);
final OutgoingFileTransfer transfer = manager
.createOutgoingFileTransfer(jid + "/Smack");
File file = new File(file_path);
try {
transfer.sendFile(file, "test_file");
} catch (XMPPException e) {
e.printStackTrace();
}
new AsyncTask<Void, Void, Void>() {
protected void onPreExecute() {
}
#Override
protected Void doInBackground(Void... params) {
while (!transfer.isDone()) {
if (transfer.getStatus().equals("Error")) {
Log.d("file transfer",
"ERROR!!! " + transfer.getError());
} else if (transfer.getStatus().equals("Cancelled")
|| transfer.getStatus().equals("Refused")) {
Log.d("file transfer",
"Cancelled!!! " + transfer.getError());
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
};
protected void onPostExecute(Void result) {
if (transfer.getStatus().equals("Refused")
|| transfer.getStatus().equals("Error")
|| transfer.getStatus().equals("Cancelled")) {
Log.i("file transfer", "refused cancelled error "
+ transfer.getError());
} else {
Log.i("file transfer", "Success: " + transfer.getFileName());
}
};
}.execute();
I had same problem, 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)
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");
Related
I want to transfer file eg. image or video from Openfire with Smack Api.
But I am getting an error code 503 service unavailable every time.
My Coding is
public void onConnectionEstablished(){
fileTransferManager = FileTransferManager.getInstanceFor(connection);
OutgoingFileTransfer.setResponseTimeout(30000);
addFileTransferListener();
}
public void sendImage(File file, String to) throws SmackInvocationException {
if (fileTransferManager == null || !isConnected()) {
throw new SmackInvocationException("server not connected");
}
EntityFullJid fullJid;
OutgoingFileTransfer transfer = fileTransferManager.createOutgoingFileTransfer(getFullJid(to));
try {
transfer.sendFile(file, file.getName());
} catch (Exception e) {
Log.e(LOG_TAG, "send file error");
throw new SmackInvocationException(e);
}
while(!transfer.isDone()) {
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
Log.d(LOG_TAG, "send file status: " + transfer.getStatus());
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
private void addFileTransferListener() {
fileTransferManager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(final FileTransferRequest request) {
new Thread() {
#Override
public void run() {
IncomingFileTransfer transfer = request.accept();
String fileName = String.valueOf(System.currentTimeMillis());
File file = new File(FileUtils.getReceivedImagesDir(context), fileName + FileUtils.IMAGE_EXTENSION);
try {
transfer.recieveFile(file);
} catch (IOException e) {
e.printStackTrace();
}
catch (SmackException e) {
Log.e(LOG_TAG, "receive file error", e);
return;
}
while (!transfer.isDone()) {
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
Log.e(LOG_TAG, "receive file error, " + transfer.getError());
return;
}
}
}
}.start();
}
});
}
My Log is
<iq type="error" id="Z6hyN-147" from="7665935694#ec2-18-221-73-31.us-east-2.compute.amazonaws.com/smack" to="8094772915#ec2-18-221-73-31.us-east-2.compute.amazonaws.com/smack"><si xmlns="http://jabber.org/protocol/si" id="jsi_1095486335648182993" mime-type="image/jpeg" profile="http://jabber.org/protocol/si/profile/file-transfer"><file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="1509942029217.jpg" size="830807"><desc>1509942029217.jpg</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"/></error></iq>
One thing i observed that when both the user sender and receiver are on the same network then not getting error.
And there is a file transfer setting in Openfire server that is given below.I donot know what it means and how to configure it.
I have seen a lot of question on stackoverflow but no answer i got.
Please help me.Thanks
I had same problem, 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 :
String to = "user1#mydomain/6u1613j3kv";
EntityFullJid fullJid = JidCreate.entityFullFrom(to);
OutgoingFileTransfer transfer = fileTransferManager.createOutgoingFileTransfer(fullJid);
6u1613j3kv string is generated on every presence change.
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.
I'm trying to transfer file using smack extension library 4.2.0 but couldn't able to successfully transfer file. When i try to transfer file this is the error I'm getting
org.jivesoftware.smack.XMPPException$XMPPErrorException: XMPP error reply received from 252615100006#server/Smack: XMPPError: service-unavailable - cancel
Even though the peer 00006 is online but I don't why I'm receiving error from the peer
This is my code to transfer file
public void sendImageMessage(String sendTo, String imagePath) throws XmppStringprepException {
FileTransferManager manager = FileTransferManager.getInstanceFor(mConnection);
EntityBareJid jid = JidCreate.entityBareFrom(sendTo);
EntityFullJid entityFullJid = JidCreate.entityFullFrom(jid+"/Smack");
Domainpart domainpart = entityFullJid.getDomain();
// Log.d(TAG ," JID Domain "+entityFullJid.do)
OutgoingFileTransfer outgoingFileTransfer = manager.createOutgoingFileTransfer(entityFullJid);
File file = new File(imagePath);
try {
outgoingFileTransfer.sendFile(file, file.getName());
} catch (SmackException e) {
e.printStackTrace();
}
while (!outgoingFileTransfer.isDone()) {
if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.error)) {
System.out.println("ERROR!!! " + outgoingFileTransfer.getError());
} else if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.cancelled)
|| outgoingFileTransfer.getStatus().equals(FileTransfer.Status.refused)) {
System.out.println("Cancelled!!! " + outgoingFileTransfer.getError());
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.refused) || outgoingFileTransfer.getStatus().equals(FileTransfer.Status.error)
|| outgoingFileTransfer.getStatus().equals(FileTransfer.Status.cancelled)) {
System.out.println("refused cancelled error" + outgoingFileTransfer.getError().toString());
} else {
System.out.println("Successfully_SEND");
}
Any help and guidance is much appreciated
Edit : I user spark client to transfer files using my server it susseccfully tranfered the file. So the problem is not with server I thin't it's in code on client side.
I had same problem, 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
user1#mydomain/Smack
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 like this:
EntityBareJid jid = JidCreate.entityBareFrom(sendTo);
EntityFullJid entityFullJid = JidCreate.entityFullFrom(jid + resource);
Where resource is resourcepart from Presence in listener.
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");
Hey i am developing Chat Application using XMPP Smack Library. Recently i am working on Group Chat While sending Group message some message will be drop so receiver wouldn't receives message from the sender side. it will gives me 400 bad request.
it is working sometimes. and sometimes not work
here i found this kind of message in 400 bad request.
<?xml version="1.0" encoding="UTF-8"?>
<message to="156#abc.com/Android" id="nXlV6-1144" type="error" from="24#confrence.abc.com/156#abc.com.com">
<received xmlns="urn:xmpp:receipts" id="nXlV6-1142" />
<error code="400" type="modify">
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
</error>
</message>
if successfully send message it will give this kind of message.
<?xml version="1.0" encoding="UTF-8"?>
<message to="156#abc.com/Android" id="nXlV6-1411" type="groupchat" from="24#conference.abc.com/156#abc.com">
<body>eyu4u4</body>
<chatDetail xmlns="jabber:x:oob">
<UID>156</UID>
<time>04:20 PM</time>
<user_icon>24_group_icon.jpg</user_icon>
<SentTime>1474368652960</SentTime>
<USERName>vasudev89</USERName>
<user_name>cryan</user_name>
<message>eyu4u4</message>
<type>group</type>
<phone_number>24</phone_number>
</chatDetail>
<request xmlns="urn:xmpp:receipts" />
</message>
how i can send message persistently? Any idea?
Thank You in Advance.
here is my code sending muc message:
public boolean sendGroupMessage(Message message, String strGroupID) {
DeliveryReceiptRequest.addTo(message);
try {
Log.i(TAG, "sendGroupMessage");
//Log.i("JOIN MUC","To join group chat: " + groupChat.getClassId());
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(AbstractXMPPConnection);
// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc = manager.getMultiUserChat(strGroupID + AppWSConstants.XMPP_JID_GROUP_CHAT_SUFFIX);
muc.sendMessage(message);
return true;
} catch (NotConnectedException e) {
e.printStackTrace();
}
return false;
}
#LearnPainLess, follow these steps to solve group chat issue
-when creating groups, save the jid of group in the database, like "somegroup#conference.{domain}.com"
-create background task for creating xmpp connection (this way it will always be connected)
-after logging in to xmpp, get the group names from the database and connect to them
also, in openfire, Group Chat > Group Chat Settings > Edit Icon > Default Room Settings > Check "Make Room Persistant"
also, in other settings > Never kick idle users
I have an XmppBase class where i put all my xmpp code
All listeners in seperate folder
Connection is stored in static variable and i retrive it using
Utils.getConnection()
// this function m calling from background service and everywhere if not connectect to xmpp
public static XMPPConnection CreateXmppConnection() {
if (Utils.getConnection() == null) {
try {
Boolean isConnected = new XmppAsync(mUsername, mPassword,context).execute().get();
if (isConnected && Utils.getConnection() != null) {
RegisterConnListeners(Utils.getConnection());
updateMyProfileImg();
// connect to all groups
DBAdapter adapter = new DBAdapter(context);
adapter.openForRead();
List<UserDetail> groups = new ArrayList<>();
adapter.addAllGroups(groups);
adapter.addPastChatGroups(groups);
adapter.close();
for(UserDetail g : groups)
{
CreateXmppMUCSession(g.getGroupTemp());
}
return Utils.getConnection();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
} else
return Utils.getConnection();
}
// get muc chat manager
public static MultiUserChatManager getMucManager() {
if(mucManager != null)
return mucManager;
if (Utils.getConnection() != null) {
return MultiUserChatManager.getInstanceFor(Utils.getConnection());
} else {
if (CreateXmppConnection() != null)
return MultiUserChatManager.getInstanceFor(Utils.getConnection());
else {
Log.v("error", "Some Error Occured");
Toast.makeText(context, "Cant Connect to Xmpp", Toast.LENGTH_SHORT).show();
return null;
}
}
}
// create muc session and m passing group name - call when you open chat page
public static void CreateXmppMUCSession(String gName)
{
RegisterGroupChatListeners(gName);
}
// connect to muc if not already connected
public static void RegisterGroupChatListeners(String groupName)
{
try {
mStateManager = getChatStateManager();
multiUserChat = getMUC(groupName);
// if(multiUserChat != null) {
multiUserChat.addMessageListener(new MyMUCMessageListener());
try {
if (!multiUserChat.isJoined()) {
DiscussionHistory discussionHistory = new DiscussionHistory();
discussionHistory.setMaxStanzas(0);
multiUserChat.join(new MyPrefrence(context).getUsername().split("#")[0], "123",
discussionHistory, SmackConfiguration.getDefaultPacketReplyTimeout());
}
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
} catch (XMPPException.XMPPErrorException e) {
e.printStackTrace();
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
// }
}
catch (Exception ex)
{
//
}
}
// get muc
public static MultiUserChat getMUC(String groupName)
{
// Log.v("nick",multiUserChat.getNickname() + " , g = " + groupName);
// if(multiUserChat != null && multiUserChat.getRoom().contains(groupName))
// {
// return multiUserChat;
// }
if (Utils.getConnection() != null) {
MultiUserChatManager chatManager = getMucManager();
if (chatManager != null) {
return chatManager.getMultiUserChat(groupName);
} else {
Toast.makeText(context, "Cannot create Chat", Toast.LENGTH_SHORT).show();
return null;
}
} else {
if (CreateXmppConnection() != null) {
MultiUserChatManager chatManager = getMucManager();
if (chatManager != null) {
return chatManager.getMultiUserChat(groupName);
} else {
Toast.makeText(context, "Cannot create Chat", Toast.LENGTH_SHORT).show();
return null;
}
}
else {
Toast.makeText(context, "Cannot create Chat", Toast.LENGTH_SHORT).show();
return null;
}
}
}
and whenever i want to send message i just call this
public static Boolean sendMUCChatMsg(Message msg)
{
if(multiUserChat != null)
try {
multiUserChat.sendMessage(msg);
return true;
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
return false;
}
Sorry if it looks clumpsy, if I missed any function there let me know, but this is working code which i am using
try this,
I modified your last function
static MultiUserChat multiUserChat;
// call this function when you open the chat window
private void CreateGroupConnection(String strGroupID ) {
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(AbstractXMPPConnection);
// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat multiUserChat = manager.getMultiUserChat(strGroupID + AppWSConstants.XMPP_JID_GROUP_CHAT_SUFFIX);
return multiUserChat;
}
// whenever sending message from chat call this
publilc static void sendMucMessage(Message message){
if(multiUserChat != null)
multiUserChat.sendMessage(message);
}
I am working on "seen and delivered" in MUC and facing with this issue when replying back with the same packet id, still testing but I think in your case you should move your xmpp connection to background service and after connecting to xmpp on device launch up, connect to all the muc in your database. This way you will always be connected to groups.
cause : according to me, when the other user is not connected to the muc and you send a message or when you reply to the group with the same packet id.
Note: I am using multiuserchat.sendmessage to send group message and chat.sendmessage to send message to single user
SMACK 4.1
** update **
I fixed it by creating new packet instead of modifying the one i am receiving
here is the packet
Message msgg = new Message();
msgg.setBody(message.getPacketID());
msgg.setSubject(MessageModel.CHAT_STATUS_SEEN + "");
XmppBase.sendMUCChatMsg(msgg);
in your case, try with simple packet first. if all works well, then add extension one by one and see where you get the error, Thanks
In My Android application I get xmpp connection initially using server_nm and 5222 port on login and done single chat message send and receive,but at time of file transfer, How I configure connection with proxy_server and 7777 port which use for file transfer?
public class XmppManager {
private static final int packetReplyTimeout = 500; // millis
private String server=server_nm;
private int port=5222;
private ConnectionConfiguration config;
private XMPPConnection connection;
String group_name,userid;
private ChatManager chatManager;
private MessageListener messageListener;
private Handler mHandler;
PacketFilter filter;
Message message;
Context context;
public static boolean IBB_ONLY = (System.getProperty("ibb") != null);//true;
protected static final String STREAM_DATA_FIELD_NAME = "stream-method";
private StreamNegotiator byteStreamTransferManager;
private StreamNegotiator inbandTransferManager;
// constructor
public XmppManager(String server, int port) {
this.server = server;
this.port = port;
}
// to initialize connection
public void init(Context c) throws XMPPException {
System.out.println(String.format(
"Initializing connection to server %1$s port %2$d", server,
port));
SmackConfiguration.setPacketReplyTimeout(50000);
config = new ConnectionConfiguration(server, port);
config.setSASLAuthenticationEnabled(false);
config.setSecurityMode(SecurityMode.disabled);
// group chat invitation
ProviderManager.getInstance().addExtensionProvider("x",
"http://jabber.org/protocol/muc#user",
new GroupChatInvitation.Provider());
// offline msgtime
ProviderManager.getInstance().addExtensionProvider("x",
"jabber:x:delay", new DelayInformationProvider());
//file transfer method
ProviderManager.getInstance().addIQProvider("query","http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
ProviderManager.getInstance().addIQProvider("query","http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
ProviderManager.getInstance().addIQProvider("query","http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
ProviderManager.getInstance().addIQProvider("si","http://jabber.org/protocol/si", new StreamInitiationProvider());
ProviderManager.getInstance().addIQProvider("open","http://jabber.org/protocol/ibb", new OpenIQProvider());
ProviderManager.getInstance().addIQProvider("close","http://jabber.org/protocol/ibb", new CloseIQProvider());
ProviderManager.getInstance().addExtensionProvider("data","http://jabber.org/protocol/ibb", new DataPacketProvider());
// v-card integration
ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new VCardProvider());
connection = new XMPPConnection(config);
connection.connect();
System.out.println("Connected: " + connection.isConnected());
chatManager = connection.getChatManager();
context = c;
Intent i = new Intent(context, MyService.class);
context.startService(i);
}
// login to xmpp server
public void performLogin(String username, String password,String resource)
throws XMPPException {
if (connection != null && connection.isConnected()) {
System.out.println("connection on presence send" + connection.isConnected() + " " + username + " " + password);
connection.login(username, password,resource);
setStatus(true, "Online");
Presence presence = new Presence(Presence.Type.available);
connection.sendPacket(presence);
}
}
//using this code I try to send file
public void sendfileafterresponse(String filenm,String filereceiverid )
{
FileTransferNegotiator.IBB_ONLY = true;
this.setServiceEnabled(connection, true);
FileTransferManager manager = new FileTransferManager(connection);
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(filereceiverid + "#chat.bobl.us/" + filereceiverid);
File file = new File(filenm);
try {
System.out.println("file send to" + filenm + filereceiverid);
transfer.sendFile(file, "test_file");
} catch (XMPPException e) {
e.printStackTrace();
}
while(!transfer.isDone()) {
if(transfer.getStatus().equals(Status.error)) {
System.out.println("ERROR!!! " + transfer.getError());
} else if (transfer.getStatus().equals(Status.cancelled)
|| transfer.getStatus().equals(Status.refused)) {
System.out.println("Cancelled!!! " + transfer.getError());
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(transfer.getStatus().equals(Status.refused) || transfer.getStatus().equals(Status.error)
|| transfer.getStatus().equals(Status.cancelled)){
System.out.println("refused cancelled error " + transfer.getError());
} else {
System.out.println("Success");
}
}
}
}
If you still want to transfer files via smack directly, you basically have too options, as far as I understand the API.
1.
First you can use In Band transmission, that is enabled via
FileTransferNegotiator.IBB_ONLY = true;
This sends your file as base64 encoded message, the same way you are sending a normal message packet.
The second option is to use a streaming proxy. This one is way faster since it does not need to encode / decode the file to base64 but transmitts the data directly. Both devices connnect to the proxy server and share data on this.
To configure a streaming proxy you have to configure it on the server side and add the Providers to your client side connection (this should be done automatically on the latest asmack library).
I configured such a setup using the ejabberd service and mod_proxy65 Mod_proxy65
add to your /etc/ejabberd/ejabberd.yml, your ip adress and host should match the service you are using.
host: "proxy1.example.org"
name: "File Transfer Proxy"
ip: "200.150.100.1"
port: 7777
Then allow the firewall to communicate on this port, and you should be good to go.
However, I figured out, that the file transfer only is functional, if both clients are in the presence "available" if any of them is in another state, the sender receives "service- unavailabe" errors.
Don't rely on XMPP file transfer using aSmack/Smack as it is buly. Instead you can create a plugin which will upload files to the server and after successfully uploading the files send the absolute url of the file to the recipient so that he/she can download the file from there.
Hope this works !
Currently I am playing around with the new Smack 4.1 that offers full support for android. Sending and receiving messages is no problem, works fine. But now, I get stuck on sending and receiving Files. For example:
Send File
public void sendFile(String fileName,String to){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
OutgoingFileTransfer transfer = transferManager.createOutgoingFileTransfer(to);
try {
transfer.sendFile(new File(fileName), "This is a Test!");
} catch (SmackException e) {
e.printStackTrace();
}
}
Receive files
public void setReceiveFileListener(){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
transferManager.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer transfer = request.accept();
try {
File file = new File(Environment.getExternalStorageDirectory() + File.separator +"TEST"+File.separator+ "new.txt");
transfer.recieveFile(file);
} catch (SmackException | IOException e) {
e.printStackTrace();
}
}
});
}
In this scenario, I just send a text file and want to save it as a new text file at sd card as "new.txt". The problem is not with the file itself, the file exists. Also, the receiver id is correct, it´s like "user#host.com/Smack" . That´s the same receiver which I can send normal messages successfully.
The error code I get from Smack:
error 503 - service unavailable
This error respone I get directly from smack after sending file and it seems the file is not transferred, because the receiver listener shows nothing (I made Logs on both, sending and receiving), but listener is surely registerred.
But I am totally sure, that File Transfer is supported by the server, it´s declared at their website www.coderollers.com . I´ve read many questions about here in SO and also at the Smack Developer Community. Nothing helped, so my question here is:
What could be the cause of this problem?
Could it be because I have to change the Port?
How to change the port of an existing connection in smack?
Any alternatives to file transport?
My normal port is 5222, that works fine to send messages. I hope someone get it work and can lead me to the right directions....thanks for helping!
SOLUTION
For all who are interested: Devendra Singhs answer is correct, with a little note by myself. It seems to be important, which resource is used. From mobile to mobile, You have to use "mobile". Wether "Smack" nor any other resource identifier would work here. So it is important to initialize the OutgoingFileTransfer correctly like this:
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer
(XmppStringUtils.completeJidFrom(USER, SERV, "mobile"));//important resource "mobile"
I got it after very research.
FileTransferManager ftm1 = FileTransferManager.getInstanceFor(connection);
FileTransferManager ftm2 = FileTransferManager.getInstanceFor(connection2);
ftm2.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer ift = request.accept();
try {
InputStream is = ift.recieveFile();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int nRead;
byte[] buf = new byte[1024];
while ((nRead = is.read(buf, 0, buf.length)) != -1) {
os.write(buf, 0, nRead);
}
os.flush();
dataReceived = os.toByteArray();
} catch (SmackException | IOException | XMPPErrorException e) {
e.printStackTrace();
}
if (Arrays.equals(dataToSend, dataReceived)) {
System.out.println("Received data matches send data. \\o/");
} else {
System.err.println("Recieved data DOES NOT match send data. :(");
}
}
});
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer(XmppStringUtils.completeJidFrom(USER, SERV, "resourse"));
oft.sendStream(new ByteArrayInputStream(dataToSend), "hello.txt", dataToSend.length, "A greeting");
outerloop: while (!oft.isDone()) {
switch (oft.getStatus()) {
case error:
System.out.println("Filetransfer error: " + oft.getError());
break outerloop;
default:
System.out.println("Filetransfer status: " + oft.getStatus() + ". Progress: " + oft.getProgress());
break;
}
Thread.sleep(1000);
}
connection.disconnect();
connection2.disconnect();
Thread.sleep(1000);
}
the one connection is sending file and another connection is receiving this is working code.
I had same problem, 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 this resource part is "mobile". Just change it with resource part from presence in roster listener.
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");