service unavailable response from server while sending custom IQ - android

I am trying to send custom information with IQ in asmack from android.
So I am using below code to send the custom IQ message.
public void onClick(View arg0) {
CustomIQ req = new CustomIQ();
req.myData="Hello world";
req.setType(IQ.Type.GET);
req.setTo(Activity_title+Constants.DOMAIN);
MainActivity.connection.sendPacket(req);
Log.d(Constants.TAG, "xml value :"+req.toXML());
Log.d(Constants.TAG, "child element value :"+req.getChildElementXML());
Log.d(Constants.TAG, " custom IQ req sent");
Below is my custom IQ class implementation:
import org.jivesoftware.smack.packet.IQ;
public class CustomIQ extends IQ {
String myData;
#Override
public String getChildElementXML() {
String request = "<query xmlns='myxmlns'>"
+ "<myData>"+ myData + "</myData>"
+ "</query>";
return request;
}
}
}
But after the sending the custom IQ, I am getting in the IQ listener as Service Unavailable and error code as 503.
Below are the request to server :
xml value :<iq id="BTn30-5" to="swathi#btp121374" type="get"><query xmlns='myxmlns'><myData>Hello world</myData></query></iq>
child element value :<query xmlns='myxmlns'><myData>Hello world</myData></query>
Below is the response from server:
xml value :<iq id="BTn30-5" to="ganesh#btp121374/Smack" from="swathi#btp121374" type="error"><query xmlns='myxmlns'><myData>Hello world</myData></query><error code="503" type="CANCEL"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
So what is the reason for I am getting response from server as Service Unavailable.
Edit:
I implemented the IQProvider as below
public class customIQProvider implements IQProvider{
#Override
public IQ parseIQ(XmlPullParser parser) throws Exception {
Log.d(Constants.TAG, "In custom IQ provider");
CustomIQ myIQ_ref = new CustomIQ();
int eventType = parser.next();
while(eventType == XmlPullParser.START_TAG){
switch(parser.getEventType()){
case XmlPullParser.START_TAG:
{
if(parser.getName().equals("myData")){
myIQ_ref.myData=(parser.nextText());
}
}
return myIQ_ref;
}
}
return null;
}
}

I believe you're falling foul of the iq routing rules in XMPP. If you send an iq stanza to "swathi#btp121374", you're not asking it to be routed to a client, you're asking the btp121374 server to handle it on behalf of swathi#btp121374.
Given the resource of the sending JID, I imagine you want to send to "swathi#btp121374/Smack" or similar. Sending it to the full JID (JID including resource) tells the server to route it to the client instead of handling it itself.
(Note that the routing rules for presence, message and iq are different - the above only applies to iq)

Obviously the server doesn't know how to handle your new namespace, unless you also implement it on the server. What do you expect the server to do with your custom element?
Also, it's generally a bad idea to generate XML by concatenating strings. If myData contains and <, or any of the other characters that need to be escaped, your XML will be invalid. Smack surely has better ways to generate your custom data.

Related

Implement receiver status(read or typing) in messaging app using smack - Android

I am creating an instant messaging app in android using smack library and openfire as a server but i cannot implement the feature of the person with whom the current user is talking to. i.e. like when user read the message or when he starts typing.
Is there any way of achieving this using smack or other ?
For knowing which user is current you must implement your own in your logic. You must use Roster (contact list in xmpp servers) to get contacts of current user and save them in database or somewhere. Then create an activity to show contacts in a list. Each contact has a unique jid that can be distinguished from others with it. So with click on each contact, send it's object(include jid) to chat-activity. In chat-activity you must get previous messages from database or MAM(archived messages in server) and you can send a message to current contact(set contact jid as To).
To achieving delivery of message you must use this link. you can set request of it with this code:
Message message = … //make your stanza
DeliveryReceiptRequest.addTo(message); //add delivery request to message
connection.sendStanza(message); //send message
then you can be notified of delivery with this code:
private void setDelRecListener() {
DeliveryReceiptManager d = DeliveryReceiptManager.getInstanceFor(connection);
d.addReceiptReceivedListener(new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
Msg msg = F.getMsgBySid(receiptId);
if (msg == null)
return;
Boolean isUpdated = F.setMsgDelivered(msg);
Log.i("m/serv/UpdateDelivery", "for: " + receiptId + (isUpdated ? " Founded&Updated" : " NotFounded"));
if (isUpdated) {
BCTool.notifyPMDelivered(msg.id, msg.conv.frnd.getBareJid());
}
}
});
}
Keep in mind that every stanza has a sid(stanza id) and you must save each corresponding sid to message model in database when send is successful. This way you can detect which message delivery you got.
- For sending chat states like composing you can use this method:
public void sendChatState(String _jid, ChatState chatState) {
try {
Message msg = new Message();
msg.addExtension(new ChatStateExtension(chatState));
msg.setTo(JidCreate.bareFrom(_jid));
msg.setType(Message.Type.chat);
connection.sendStanza(msg);
Log.e("m/service", "ChatStateSent");
} catch (SmackException.NotConnectedException | InterruptedException | XmppStringprepException e) {
Log.e("m/service", "ChatState Not Sent: " + e.getMessage());
e.printStackTrace();
}
}
You must set a timer to prevent send composing in next 5Sec and reset timer when a character typed.
Consider reading this: ChatStateNotifications

Smack receiving messages in XML format can i change them to JSON?

I am developing an android chatting app with the use of Openfire as a support server for XMPP and smack library as a android implementation of XMPP.
Things are going well. Till i find this received message from another user. The format is like this :
<message to="rajesh2#peacock-hp" id="0mpqe-10" type="chat" from="rajesh1#peacock-hp/Smack">
<body>{"Date":"8 Jul 2016","Time":"0:40p.m.","body":" vhklv","isMine":true,"msgid":"909-08","receiver":"rajesh2","sender":"rajesh1","senderName":"rajesh1"}</body>
<thread>06ed73bb-21ad-4276-80cb-0ea4fc9d9dfb</thread>
</message>
My listener which is receiving messages :
private class MMessageListener implements ChatMessageListener {
public MMessageListener(Context contxt) {
}
#Override
public void processMessage(final org.jivesoftware.smack.chat.Chat chat,
final Message message) {
Log.i("MyXMPP_MESSAGE_LISTENER", "Xmpp message received: '"
+ message);
}
}
My Question is : Can i receive this message in JSON format instead of
XML ??
As I am learning smack and xmpp please guide me if i am wrong at some places. correct me if any one of you find me wrong.
You can convert messages to JSON format through a project on Github.
Example :
public class Main {
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
public static String TEST_XML_STRING =
"<?xml version=\"1.0\" ?><test attrib=\"moretest\">Turn this to JSON</test>";
public static void main(String[] args) {
try {
JSONObject xmlJSONObj = XML.toJSONObject(TEST_XML_STRING);
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
System.out.println(jsonPrettyPrintString);
} catch (JSONException je) {
System.out.println(je.toString());
}
}
}
Output is:
{
"test": {
"attrib": "moretest",
"content": "Turn this to JSON"
}
}
Credit goes to Quickest way to convert XML to JSON in Java
Json it's not the reply format for Openfire.
Of course, you can rewrite all Openfire to "talk" in Json, but to me has no sense.
What I suggest to you:
if you have a performance issue, you can look maybe for Ejabber
If you need a Json, maybe Prosody IM has a plugin
If you don't want to broke your head with XmlPullParser, give a try
to Bubbler in alternative to Smack.
If you just don't feel confident with XML, just implement a "toJson" for each Stanza type so you'll have something like
(thanking #Khan)
MyMessage extends Message
public String toJson()
{
JSONObject xmlJSONObj = XML.toJSONObject(this.toXML());
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
}
and you'll be able to use a Json.

How to send message to notification key with GCM without an app server

I followed Google Cloud Messaging (GCM) with local device groups on Android gives HTTP Error code 401 to manage local device groups on Android and successfully got a notification key, but when I send message to the notification key, I never get the message back.
Has anyone ever got this work?
My send code is like:
public void sendMessage(View view) {
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
try {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
String to = notificationKey; // the notification key
AtomicInteger msgId = new AtomicInteger();
String id = Integer.toString(msgId.incrementAndGet());
Bundle data = new Bundle();
data.putString("hello", "world");
gcm.send(to, id, data);
Log.e(TAG, "sendMessage done.");
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
return null;
}
};
task.execute();
}
It seems there's a misunderstanding about the GCM concept. The app server is an integral part of GCM messaging.
The server side of Google Cloud Messaging (GCM) consists of two
components:
GCM connection servers provided by Google. These servers take messages
from an app server and send them to a client app running on a device.
Google provides connection servers for HTTP and XMPP.
An application
server that you must implement in your environment. This application
server sends data to a client app via the chosen GCM connection
server, using the appropriate XMPP or HTTP protocol.
Try the Android GCM Playground to get a better understanding of this.
Here's a snippet:
public void sendMessage() {
String senderId = getString(R.string.gcm_defaultSenderId);
if (!("".equals(senderId))) {
String text = upstreamMessageField.getText().toString();
if (text == "") {
showToast("Please enter a message to send");
return;
}
// Create the bundle for sending the message.
Bundle message = new Bundle();
message.putString(RegistrationConstants.ACTION, RegistrationConstants.UPSTREAM_MESSAGE);
message.putString(RegistrationConstants.EXTRA_KEY_MESSAGE, text);
try {
gcm.send(GcmPlaygroundUtil.getServerUrl(senderId),
String.valueOf(System.currentTimeMillis()), message);
showToast("Message sent successfully");
} catch (IOException e) {
Log.e(TAG, "Message failed", e);
showToast("Upstream FAILED");
}
}
}
The to field of the send method represents the sender ID of your project. You cannot use this method to send messages to Instance ID tokens (other devices), Device to Device messaging is not currently supported by GCM.
You are correct to avoid including the API key in your client app, so currently you will need an app server to send these types of messages.

Received GCM message shows with garbled text

I have a Servlet deployed on Google App Engine, which is playing the role of posting broadcast message to GCM. Android clients will receive that broadcast message from GCM. The Servlet extends BaseServlet with following snippet.
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//when receiving a gcm broadcast request, send message to GCM
Builder mb = new Message.Builder();
mb.addData("message", "The message to send");
Message message = mb.build();
sender.sendNoRetry(message, regIds);
...
}
When "the message to send" is in English, all things fine. But if "the message to send" is replaced by other language such as Chinese, the Android client will receive a string of garbled text. On Android client, I use a class extends GCMBaseIntentService to deal with GCM broadcast.
#Override
protected void onMessage(Context context, Intent intent) {
String message = "";
message = intent.getStringExtra("message")!=null ? intent.getStringExtra("message") : "";
doNotify(message);
}
I have tried to re-encode the message but doesn't work.
message = new String(message.getBytes("ISO-8859-1"), "UTF-8");
Have any idea about the problem? I need your help, Thanks.
Try URLEncoder
mb.addData("message", URLEncoder.encode("世界","UTF-8");
another option:
mb.addData("message", new StringEntity("世界", "UTF-8");
After looking at the source code of GCM : com.google.android.gcm.server.Sender , it is useing HttpPost as json, and Java uses UTF-16 to internally so before you post, you will need to encode it properly.
And as comment stated, at client decode the String
String yourAwesomeUnicodeString=URLDecoder.decode(intent.getStringExtra("message"),"UTF-8");

SIP Stack registration packet with authorization

we want to create SIP application on Android 2.3.3 and have some issues with android.sip stack (default sip stack). Our mobile app sends register sip packet, but
1.) by default OpenIMS core responds 400 Bad request P-Visited-Network-ID Header missing
2.) in the case that we set port number to 4060 -PCSCF /builder.setPort(4060)/ OpenIMS core sends this request from 4060 to 4060 (same port, same IP, same CSCF, same packet) and this is cykling until OpenIMS core send respond to mobile app - 504 Server Time-out.
We also tried SipDemo, CSipSimple and we had same problems.
When we tried Monster Communicator or IMSDroid, then it works!
There is one difference between working and problematic applications - working apps send register packet also with Authorization field.
Part of the code:
public SipManager mSipManager = null;
public SipProfile mSipProfile = null;
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
builder.setDisplayName(username);
builder.setProfileName(username + "#" + domain);
port = Integer.parseInt(4060);
builder.setProtocol(protocol);
mSipProfile = builder.build();
...
try { mSipManager.open(mSipProfile);} catch (SipException e) { ...}
try {
mSipManager.register(mSipProfile, 30, new SipRegistrationListener(){
public void onRegistering(String localProfileUri) {
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
}
public void onRegistrationFailed(String localProfileUri, int errorCode, String errorMessage) {
}
});
} catch (SipException e) {
....
}
How to give authorization field to register packet in classic SIP stack?
We also tried J-SIP but it display error: Conversion to dalvik format failed with error 1.
Every answer would be very appreciated.
Your problem is not related to missing Authorization header.
Registration is done in the following matter:
the client send Register request without "Authorization" header.
server response with 401 response code which includes an header named "WWW-Authnticate", that header hold parameters as realm, opaque, qop and hashing algorithm type.
using these parameters with the username and passord an Authorication header is generated automatically by SIP stacks. and a second Register request is sent which includes the "Authorication" header.
the if the request is send in the correct manner the server return 200 OK response code which means that you are now registered.
Your problem is something else, you don't even get to step 3 (Authorization step), you fail in step 1, for your initial Register request you receive 400 Bad Request response code - which almost always mean that you have a syntax error in your request.

Categories

Resources