MGTalk cannot connect to Wildfire 3.1.0

I have raised the following bug on MGTalk’'s project page

http://sourceforge.net/tracker/index.php?func=detail&aid=1580773&group_id=165489 &atid=835597

I have been using MGTalk on a Nokia 6230i in the UK,

connecting to my own Wildwire server (version 2.6.1).

(http://www.jivesoftware.org/wildfire/)

Recently I’'ve upgraded that to 3.1.0, and for some

reason, MGTalk will no longer connect.

The problem appears to be that the stream closes, I suspect to MGTalk’'s surprise, which is why it suddenly disconnects.

I can’‘t find anyone else reporting anything with MGTalk using Wildfire on the forums, or anywhere else on the net for that matter. I have an old server running 2.5.x and 2.6.1, and both are working ok, it’'s since the 3.1.0 upgrade that things stopped working.

I checked through the change logs, to see if I could spot anything regarding compression or default setting changes, but I can’'t see anything that looks like it would obviously cause a problem.

If anyone has any ideas, suggestions or advice, please help…

Cheers,

Mark

I am not sure yet how to fix this, but I did get it working.

Two things need to be done in NetworkThread.java:

  1. Uncomment connection settings to:

conn = (SocketConnection) Connector.open(addr);

conn.setSocketOption(SocketConnection.LINGER, 0);

conn.setSocketOption(SocketConnection.SNDBUF, 30);

conn.setSocketOption(SocketConnection.RCVBUF, 30);

conn.setSocketOption(SocketConnection.KEEPALIVE, 1);

2 In the method startSession:

Delete the MD5 if statement, keeping the plain text part.

It appears that the new wildfire is not sending the correct digest info:

Here is my new NetworkThread.java:

import java.io.DataInputStream;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.Date;

import java.util.Vector;

import javax.microedition.io.Connection;

import javax.microedition.io.Connector;

import javax.microedition.io.HttpConnection;

import javax.microedition.io.HttpsConnection;

import javax.microedition.io.SocketConnection;

import com.twmacinta.util.MD5;

/**

  • @author Vorobev
  • Main class for network packets processing

*/

public class NetworkThread extends Thread{

private boolean ended = false; //Network error flag

private boolean busy = false; //Indicates if someone reads packet

private boolean google = false; //Google Talk server flag

private boolean terminated = false; //Indicates if someone closed connection

private boolean statusSet = false; //Indicates that status text from profile already has been set

private String token = “”; //Google token holder

private boolean inCycle = false;

/**

  • Converts String to UTF-8 String. Code from Colibry IM messenger used

  • @param s String to convert

  • @return converted String

*/

public static String ToUTF(String s) {

int i = 0;

StringBuffer stringbuffer = new StringBuffer();

for (int j = s.length(); i < j; i++) {

int c = (int) s.charAt(i);

if ((c >= 1) && (c <= 0x7f)) {

stringbuffer.append((char) c);

}

if (((c >= 0x80) && (c <= 0x7ff)) || (c == 0)) {

stringbuffer.append((char) (0xc0 | (0x1f & (c >> 6))));

stringbuffer.append((char) (0x80 | (0x3f & c)));

}

if ((c >= 0x800) && (c <= 0xffff)) {

stringbuffer.append(((char) (0xe0 | (0x0f & (c >> 12)))));

stringbuffer.append((char) (0x80 | (0x3f & (c >> 6))));

stringbuffer.append(((char) (0x80 | (0x3f & c))));

}

}

return stringbuffer.toString();

}

private void reconnect() {

ended = true;

if(log.getProfile().getAutoReconnect()>0)

log.newSession(log.getDisplay(), log.getProfile());

}

/*

  • Terminates current NetworkThread softly

  • Closes connection, informs user about it, and sets ended flag

  • */

public synchronized void terminate() {

terminate(true);

}

public synchronized void terminate(boolean play) {

if(terminated)

return;

try {

log.addMessage(“Disconnected”);

} catch (Exception e2) {

}

ended = true;

terminated = true;

try {

conn.close();

is.close();

os.close();

} catch (Exception e) {

// TODO Auto-generated catch block

// e.printStackTrace();

System.out.println(“Ex1”);

}

try {

log.setCurrent();

} catch (Exception e1) {

// TODO Auto-generated catch block

// e1.printStackTrace();

System.out.println(“Ex2”);

}

notify();

System.out.println(“Ended set to true”);

try {

Thread.sleep(510);

if(play)

reconnect();

} catch (Exception e) {

// e.printStackTrace();

System.out.println(“Ex3”);

}

}

// private RosterList list = null;

private ConnectLog log;

/**

  • Constructs thread and starts it

  • @param l

*/

public NetworkThread(ConnectLog l) {

log = l;

start();

}

/* (non-Javadoc)

  • @see java.lang.Runnable#run()
  • Main method that processes jabber packets

*/

public void run() {

log.addMessage(“Connecting…”);

/*

  • Username must consists of 3 parts: @

  • Split username and extract user and domain values

  • */

String user = log.getProfile().getUser();

if(user.indexOf("@")==-1) {

log.addMessage("Invalid username "+user);

reconnect();

return;

}

String domain = user.substring(user.indexOf("@")+1, user.length());

user = user.substring(0, user.indexOf("@"));

/*

  • Constructs Jabber server address

  • */

String addr = log.getProfile().getHost()+":"+log.getProfile().getPort();

if(log.getProfile().getSsl()==1)

addr = “ssl://”+addr;

else

addr = “socket://”+addr;

/*

  • If user wants to work with Google server - generate it

  • */

if(log.getProfile().getIsGoogle()>0){

if(log.getProfile().getUseMyServer()>0)

token = getGoogleTokenViaMyServer(log.getProfile().getUser(), log.getProfile().getPass());

else

token = getGoogleToken(log.getProfile().getUser(), log.getProfile().getPass());

}

if(ended) {

reconnect();

return;

}

try {

/*

  • Starts session with jabber server

  • */

startSession(addr, domain, user, log.getProfile().getPass(), “Mobile”, log.getProfile().getStatus());

}catch(Exception e) {

//If any exception throws - terminate connection

log.addMessage(e.getMessage());

reconnect();

return;

}

if(ended) {

reconnect();

return;

}

log.addMessage("Successfully connected with "+log.getProfile().getUser());

String show = “”;

if(log.getProfile().getStatusID()==1)

show = “away”;

if(log.getProfile().getStatusID()==2)

show = “xa”;

if(log.getProfile().getStatusID()==3)

show = “dnd”;

//Calculates string representation of initial user status

if(isGoogle()) {

//Sets important Google settings

if(ended) {

reconnect();

return;

}

System.out.println(“isGoogle”);

writeToAir("");

//Informs Google Talk that we want to use GTalk features

XmlNode y = readStanza();

if(y.getAttr(“type”).equals(“error”)) {

//Sorry

ended = true;

} else {

writeToAir("");

//Sends mail notification request to GTalk server

y = readStanza();

if(y.getAttr(“type”).equals(“error”)) {

//Sorry

ended = true;

}

}

}

if(!ended) {

if(!isGoogle())

writeToAir("<query xmlns=“google:mail:notify” q="(!label:s) (!label:k) ((label:u) (label:i) (!label:^vm))"/>");

/*

    1. Sets empty status and status text for GTalk server -
  • GTalk will return last/current status and status text

    1. Requests new mail count and mail info
    1. Requests google shared status lists
  • */

}

}

long nowTime = new Date().getTime();

inCycle = true;

log.initRoster();

log.getRoster().getRoster().setTitle(log.getProfile().getUser());

while(!ended) {

//Main cycle

try {

if((is.available()>0)&&(!busy)) {

//if nobody reads packet and data is ready - get packet

XmlNode x = readOneStanza();

if(x.getName().equals("")) {

//Data empty - continue sleep

continue;

}

{

if(x.getName().equals(“iq”)&&x.child(“query”).getAttr(“xmlns”).equals(“jabber:i q:roster”)) {

//Data about contact received. Processes all “query” childs and updates info abount contact

Vector v = x.child(“query”).getChilds();

log.getRoster().setFullJid(x.getAttr(“to”));

for(int i=0; i<v.size() ;i++) {

XmlNode y = (XmlNode) v.elementAt(i);

//Show contacts only with “both” and “to” subscriptions

if(y.getAttr(“subscription”).equals(“both”))

log.getRoster().getRosterFactory().updateContact(y.getAttr(“jid”).toLowerCase() , y.getAttr(“name”), null, null);

}

}

if(x.getName().equals(“presence”)) {

//We are here if someone changes own status

log(“Need to change presence”);

if(x.getAttr(“type”).equals(“subscribe”)) {

//User requests authorization - Ask for decision from user

log.getRoster().playMessage();

new RequestAuth(log.getDisplay(),this, log.getDisplay().getCurrent(), x.getAttr(“from”), log.getProfile().getUser());

}

//Updates contact info in a roster

log.getRoster().getRosterFactory().updateContact(x.getAttr(“from”).toLowerCase( ), null, x.getAttr(“type”).equals("")?x.childValue(“show”):x.getAttr(“type”), x.childValue(“status”));

}

if(x.getName().equals(“message”)&&!x.getAttr(“type”).equals(“error”)) {

//We received a Message, Lets RosterFactory process it!

log.getRoster().getRosterFactory().addMessage(x.getAttr(“from”).toLowerCase(), x.childValue(“body”), x.getAttr(“id”));

// list.getDisplay().vibrate(1000);

}

if(x.getName().equals(“iq”)&&x.child(“query”).getAttr(“xmlns”).equals(“jabber:i q:version”)) {

//We received unsupported packet

System.out.println(“Proceed”);

writeToAir("<iq type=“error” to="“x.getAttr(“from”)”">0) {

//Start MailViewer and play sound

log.getRoster().getMails().startMe();

log.getRoster().playMessage();

}

}

if(x.getName().equals(“iq”) && isGoogle() && x.child(“new-mail”).getAttr(“xmlns”).equals(“google:mail:notify”)) {

/*

  • New mail notification. Requests new mails newer then remebered data and ID if this fields were stored before

*/

writeToAir("<query xmlns=“google:mail:notify”"(log.getRoster().getLastTime().equals("")?"":" newer-than-time="“log.getRoster().getLastTime()”"")(log.getRoster().getTid().equals("")?"":" newer-than-tid="“log.getRoster().getTid()”"")+" q="(!label:s) (!label:k) ((label:u) (label:i) (!label:^vm))"/>");

}

if(x.getName().equals(“iq”) && isGoogle() && x.child(“query”).getAttr(“xmlns”).equals(“google:shared-status”)) {

//We receive google:shared:list and current status ans status text

//If we dont want to set custom status - we store received status text

if(statusSet || log.getProfile().getLockStatusStr()==0)

log.getProfile().setStatus(x.child(“query”).childValue(“status”));

//Next, we calculate new status ID

int newStatus = x.child(“query”).childValue(“show”).equals("")?0:

(x.child(“query”).childValue(“show”).equals(“away”)?1:3);

//Clears all status holders

log.getRoster().getOnlines().removeAllElements();

log.getRoster().getBusies().removeAllElements();

log.getRoster().getAways().removeAllElements();

Vector v = x.child(“query”).getChilds();

//This routine parses received packet and fills status holders

for(int i=0; i<v.size(); i++) {

XmlNode t = (XmlNode) v.elementAt(i);

if(t.getName().equals(“status-list”)) {

Vector v2 = t.getChilds();

for(int j=0; j<v2.size(); j++) {

XmlNode t2 = (XmlNode) v2.elementAt(j);

if(t.getAttr(“show”).equals(“dnd”))

log.getRoster().getBusies().addElement(t2.getValue());

else

if(t.getAttr(“show”).equals(“away”))

log.getRoster().getAways().addElement(t2.getValue());

else

log.getRoster().getOnlines().addElement(t2.getValue());

}

}

}

if(log.getProfile().getStatusID()==2)

log.getProfile().setStatusID(1);

//Fix status ID for GTalk server (GTalk does not support XA status) I think so

//First if - we return our status back if lockStatus selected in profile by sending packet with old status

//Otherwise we store new status in profile

if(newStatus!=log.getProfile().getStatusID() &&

log.getProfile().getLockStatus()>0)

generatePresense();

else

log.getProfile().setStatusID(newStatus);

//If we received status info first time and if we want to set custom status text - it’'s time to do this

if(!statusSet && log.getProfile().getLockStatusStr()>0) {

statusSet = true;

generatePresense();

}

}

}

} else {

if(!busy) {

if(new Date().getTime()-nowTime>600000) {

show = “”;

if(log.getProfile().getStatusID()==1)

show = “away”;

if(log.getProfile().getStatusID()==2)

show = “xa”;

if(log.getProfile().getStatusID()==3)

show = “dnd”;

nowTime = new Date().getTime();

writeToAir("<presence from="“log.getRoster().getFullJid()”">“show”“log.getProfile().getStatusStr()”");

}

}

Thread.sleep(500);

//Wait for next packet

}

} catch (Exception e) {

ended = true;

//Sorry

}

}

terminate();

}

private SocketConnection conn;

private boolean isSecure = false;

private InputStream is = null;

private OutputStream os = null;

/**

  • This routine reads next packet from stream. if we receive empty packet - we will try to do this later

  • @return

*/

public XmlNode readStanza() {

busy = true;

XmlNode x = new XmlNode();

if(ended) {

terminate();

return x;

}

do

{

if(!ended) {

try {

x.init("", is);

} catch (Exception e) {

ended = true;

}

busy = false;

}

}

while(x.getName().equals("") && !ended);

System.out.println(x);

return x;

}

/**

  • This routine siply reads next packet from stream

  • @return

*/

public XmlNode readOneStanza() {

busy = true;

XmlNode x = new XmlNode();

if(ended) {

terminate();

return x;

}

if(!ended) {

try {

x.init("", is);

} catch (Exception e) {

ended = true;

}

busy = false;

}

System.out.println(“RO:”+x);

return x;

}

/**

  • Base16 encodes input string

  • @param s input string

  • @return output string

*/

private String Base16Encode(String s) {

String res = “”;

for(int i=0; i<s.length(); i++) {

res += Integer.toHexString(s.charAt(i));

}

return res;

}

/**

  • This routine writes packet to stream

  • If Exception thrown, terminate is called

  • @param mess

*/

public void writeToAir(String mess) {

if(ended) {

terminate();

return;

}

try {

if(os!=null) {

os.write(ToUTF(mess).getBytes());

os.flush();

}

} catch (Exception e) {

ended = true;

}

}

private Connection getConnection() {

return conn;

}

/**

  • Establishes connection with Jabber server

  • @param addr

  • @throws Exception

*/

private void initConnection(String addr) throws Exception {

try {

log.addMessage("Init connection to "+addr);

conn = (SocketConnection) Connector.open(addr);

conn.setSocketOption(SocketConnection.LINGER, 0);

conn.setSocketOption(SocketConnection.SNDBUF, 30);

conn.setSocketOption(SocketConnection.RCVBUF, 30);

conn.setSocketOption(SocketConnection.KEEPALIVE, 1);

is = conn.openInputStream();

os = conn.openOutputStream();

isSecure = false;

// return true;

} catch (Exception e) {

// TODO Auto-generated catch block

throw new Exception(e.getMessage());

}

// return false;

}

/**

  • This routine generates MD5-DIGEST response via SASL specification

  • @param user

  • @param pass

  • @param realm

  • @param digest_uri

  • @param nonce

  • @param cnonce

  • @return

*/

private String generateAuthResponse(String user, String pass, String realm, String digest_uri, String nonce, String cnonce) {

String val1 = user*":“realm”:"*pass;

byte bb[] = new byte[17];

bb = md5It(val1);

int sl = new String(":“nonce”:"+cnonce).length();

byte cc[] = new String(":“nonce”:"+cnonce).getBytes();

byte bc[] = new byte[99];

for(int i=0; i<16; i++) {

bc[i] = bb[i];

}

for(int i=16; i<sl16; i+) {

bc[i] = cc[i-16];

}

String val2 = new String(MD5.toHex(md5It(bc, sl+16)));

String val3 = “AUTHENTICATE:”+digest_uri;

val3 = MD5.toHex(md5It(val3));

String val4 = val2*":“nonce”:00000001:“cnonce”:auth:"*val3;

// System.out.println("Before auth = “val4”, val1 = "+val1);

val4 = MD5.toHex(md5It(val4));

// System.out.println("Val4 = "+val4);

String enc = “charset=utf-8,username=”“user”",realm="“realm”"," +

“nonce=”“nonce”",cnonce="“cnonce”"," +

“nc=00000001,qop=auth,digest-uri=”“digest_uri”"," +

“response=”+val4;

String resp = MD5.toBase64(enc.getBytes());

return resp;

}

/**

  • MD5 routines

  • @param s

  • @return

*/

public byte[] md5It(String s) {

byte bb[] = new byte[16];

try {

MD5 md2 = new MD5(s.getBytes());

return md2.doFinal();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return bb;

}

public byte[] md5It(byte[] s, int l) {

byte bb[] = new byte[16];

try {

byte tmp[] = new byte[l];

for(int i=0; i<l;i++) {

tmp[i] = s[i];

}

MD5 md2 = new MD5(tmp);

return md2.doFinal();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return bb;

}

private void log(String s) {

// System.out.println(s);

}

/**

  • Service routine

  • @param dis

  • @return

*/

private String readLine(DataInputStream dis) {

String s = “”;

byte ch = 0;

try {

while((ch = dis.readByte())!=-1) {

// System.out.println("ch = "+ch);

if(ch==’’\n’’)

return s;

s += (char)ch;

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return s;

}

/**

  • Generates X-GOOGLE-TOKEN response by communication with http://www.google.com

  • @param userName

  • @param passwd

  • @return

*/

private String getGoogleToken(String userName, String passwd) {

String first = “Email=“userName”&Passwd=“passwd”&PersistentCookie=false&source=googletalk”;

try {

        HttpsConnection c = (HttpsConnection) Connector.open("https://www.google.com:443/accounts/ClientAuth?"+first);

log.addMessage(“Connecting to www.google.com”);

DataInputStream dis = c.openDataInputStream();

String str = readLine(dis);

String SID = “”;

String LSID = “”;

if(str.startsWith(“SID=”)&&!ended) {

SID = str.substring(4, str.length());

str = readLine(dis);

LSID = str.substring(5, str.length());

first = “SID=“SID”&LSID=“LSID”&service=mail&Session=true”;

dis.close();

c.close();

            c = (HttpsConnection) Connector.open("https://www.google.com:443/accounts/IssueAuthToken?"+first);

log.addMessage(“Next www.google.com connection”);

dis = c.openDataInputStream();

str = readLine(dis);

String token = MD5.toBase64(new String("\0"userName"\0"+str).getBytes());

dis.close();

c.close();

return token;

} else

throw new Exception(“Invalid response”);

}catch(Exception ex) {

ex.printStackTrace();

System.out.println("EX: "+ex.toString());

}

return “”;

}

/**

  • Generates X-GOOGLE-TOKEN response by communication with http://www.google.com

  • @param userName

  • @param passwd

  • @return

*/

private static String MY_SERVER = "http://temp.27-i.net/servlet/GenerateToken?";

private String getGoogleTokenViaMyServer(String userName, String passwd) {

String first = “email=“userName”&pass=”+passwd;

try {

HttpConnection c = (HttpConnection) Connector.open(MY_SERVER+first);

log.addMessage(“Connecting to help server…”);

DataInputStream dis = c.openDataInputStream();

String str = readLine(dis);

if(!str.equals("")&&!ended) {

dis.close();

c.close();

return str;

} else

throw new Exception(“Invalid response”);

}catch(Exception ex) {

ex.printStackTrace();

System.out.println("EX: "+ex.toString());

}

return “”;

}

/**

  • Initializes session with Jabber server

  • @param addr

  • @param domain

  • @param user

  • @param pass

  • @param resource

  • @param Status

  • @throws Exception

*/

private void startSession(String addr, String domain, String user, String pass, String resource, String Status) throws Exception {

try {

initConnection(addr);

// throw new Exception(“Cannt connect”);

log.addMessage(“Opening first stream”);

log(“Initiate stream”);

writeToAir("<?xml version=\"1.0\"?><stream:stream to="“domain”" xmlns=“jabber:client” xmlns:stream=“http://etherx.jabber.org/streams” version=“1.0”>");

XmlNode x = readStanza();

if(x.getName().equals(“stream:error”))

throw new Exception(“Error opening stream”);

log(x.toString());

log.addMessage(“Authenticating”);

//PLAIN authorization supported by GTalk server in SSL mode

log(“Using plain authorization”);

String resp = “\0"user”\0"+pass;

writeToAir("“MD5.toBase64(resp.getBytes())”");

log.addMessage(“Starting PLAIN authorization”);

x = readStanza();

if(x.getName().equals(“failure”))

throw new Exception(“PLAIN authorization error”);

        writeToAir("<?xml version=\"1.0\"?><stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\" to=\""*domain*"\" version=\"1.0\">");

log.addMessage(“Opening next stream”);

x = readStanza();

if(x.getValue().equals(“stream:error”))

throw new Exception(“Error opening second stream”);

log(“Binding resource”);

//Resource binding and session establishing

writeToAir("“resource”");

log.addMessage(“Binding resource”);

x = readStanza();

if(x.getAttr(“type”).equals(“error”))

throw new Exception(“Error binding resource”);

writeToAir("<iq to="“domain”" type=“set” id=“sess_1”>" +

“”);

log.addMessage(“Opening session”);

x = readStanza();

if(x.getAttr(“type”).equals(“error”))

throw new Exception(“Error opening session”);

} catch (Exception e) {

// TODO Auto-generated catch block

log("Exception found: "+e.getMessage());

throw new Exception(e.getMessage());

}

}

/**

  • @return Returns the list.

*/

/**

  • @return Returns the google.

*/

public boolean isGoogle() {

return google;

}

/**

  • @param google The google to set.

*/

public void setGoogle(boolean google) {

this.google = google;

}

/**

  • This routine generates presense packet

*/

public void generatePresense() {

String outp = “”;

String show = “”;

if(log.getProfile().getStatusID()==1)

show = “away”;

if(log.getProfile().getStatusID()==2)

show = “xa”;

if(log.getProfile().getStatusID()==3)

show = “dnd”;

if(!isGoogle()) {

//Very simple

outp = ““show”“log.getProfile().getStatus()””;

} else {

//Very hard

//First, insert into response current status and status text

outp = “<iq type=“set” to=”“log.getProfile().getUser()”">" +

""log.getProfile().getStatus()

““show””;

//Next, collect XML stream by processing all status holders and adding new status text if custom status was set

String s = “”;

boolean found = false;

for(int i=0; i<log.getRoster().getOnlines().size(); i++) {

s*=""*log.getRoster().getOnlines().elementAt(i).toString()+"“log.getProfile().getStatus()”" +s;

outp *= “<status-list show=“default”>”*s+"";

s = “”;

found = false;

for(int i=0; i<log.getRoster().getBusies().size(); i++) {

s*=""*log.getRoster().getBusies().elementAt(i).toString()+"“log.getProfile().getStatus()”" +s;

outp *= “<status-list show=“dnd”>”*s+"";

s = “”;

found = false;

for(int i=0; i<log.getRoster().getAways().size(); i++) {

s*=""*log.getRoster().getAways().elementAt(i).toString()+"“log.getProfile().getStatus()”" +s;

outp *= “<status-list show=“away”>”*s+"";

outp+="";

}

writeToAir(outp);

//Writes response to stream

}

/**

  • @return Returns the inCycle.

*/

public boolean isInCycle() {

return inCycle;

}

/**

  • @param inCycle The inCycle to set.

*/

public void setInCycle(boolean inCycle) {

this.inCycle = inCycle;

}

/**

  • @return Returns the ended.

*/

public boolean isEnded() {

return ended;

}

/**

  • @param ended The ended to set.

*/

public void setEnded(boolean ended) {

this.ended = ended;

}

}