I am not sure yet how to fix this, but I did get it working.
Two things need to be done in NetworkThread.java:
- 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;
/**
- 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;
/**
*/
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;
/**
*/
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…”);
/*
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("@"));
/*
String addr = log.getProfile().getHost()+":"+log.getProfile().getPort();
if(log.getProfile().getSsl()==1)
addr = “ssl://”+addr;
else
addr = “socket://”+addr;
/*
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 {
/*
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))"/>");
/*
}
}
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;
/**
*/
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;
}
/**
*/
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;
}
/**
*/
private String Base16Encode(String s) {
String res = “”;
for(int i=0; i<s.length(); i++) {
res += Integer.toHexString(s.charAt(i));
}
return res;
}
/**
*/
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;
}
/**
*/
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;
}
/**
*/
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;
}
/**
*/
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 “”;
}
/**
*/
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 “”;
}
/**
*/
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;
}
}