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: <user>@<domain>
* 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("<iq type=\"get\" id=\"6\"><query xmlns=\"google:relay\"/></iq>");
//Informs Google Talk that we want to use GTalk features
XmlNode y = readStanza();
if(y.getAttr("type").equals("error")) {
//Sorry :(
ended = true;
} else {
writeToAir("<iq type=\"set\" to=\""+log.getProfile().getUser()+"\" id=\"15\"><usersetting xmlns=\"google:setting\"><autoacceptrequests value=\"false\"/>"+
"<mailnotifications value=\"true\"/></usersetting></iq>");
//Sends mail notification request to GTalk server
y = readStanza();
if(y.getAttr("type").equals("error")) {
//Sorry :(
ended = true;
}
}
}
if(!ended) {
if(!isGoogle())
writeToAir("<presence><show>"+show+"</show><status>"+log.getProfile().getStatus()+"</status></presence>");
//Send current status and status text to non Google Talk servers
writeToAir("<iq type=\"get\" id=\"roster\">" +
"<query xmlns=\"jabber:iq:roster\"/></iq>");
//Requests roster items (for all servers)
if(isGoogle()) {
writeToAir("<presence><show></show><status></status></presence>");
writeToAir("<iq type=\"get\" id=\"23\"><query xmlns=\"google:mail:notify\" q=\"(!label:^s) (!label:^k) ((label:^u) (label:^i) (!label:^vm))\"/></iq>" +
"<iq type=\"get\" to=\""+log.getProfile().getUser()+"\" id=\"21\"><query xmlns=\"google:shared-status\"/></iq>");
/*
* 1. Sets empty status and status text for GTalk server -
* GTalk will return last/current status and status text
* 2. Requests new mail count and mail info
* 3. 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:iq: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:iq:version")) {
//We received unsupported packet
System.out.println("Proceed");
writeToAir("<iq type=\"error\" to=\""+x.getAttr("from")+"\"><query xmlns=\"jabber:iq:version\"/><error code=\"501\" type=\"cancel\"><feature-not-implemented xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/></error></iq>");
}
if(x.getName().equals("iq") && isGoogle() && !x.child("mailbox").getAttr("result-time").equals("")) {
//New received mail response. Processes all mails and adds it to MailViewer
Vector v = x.child("mailbox").getChilds();
//Newer mails must be on top
for(int i=v.size()-1; i>=0; i--) {
XmlNode t = (XmlNode) v.elementAt(i);
if(i==0) {
//Last mail. Remebers mail date and mail ID
log.getRoster().setLastTime(t.getAttr("date"));
log.getRoster().setTid(t.getAttr("tid"));
}
log.getRoster().getMails().addMail(t.child("senders").child("sender").getAttr("name"), t.childValue("subject"), t.childValue("snippet"));
}
if(v.size()>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("<iq type=\"get\"><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))\"/></iq>");
}
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>"+show+"</show><status>"+log.getProfile().getStatusStr()+"</status></presence>");
}
}
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<sl+16; 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");
if(x.child("mechanisms").hasValueOfChild("DIGEST-MD5")) {
//DIGEST-MD5 authorization doing
log("MD5 authorization doing");
writeToAir("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"DIGEST-MD5\"/>");
log.addMessage("Sending authorization data");
x = readStanza();
if(x.getName().equals("failure"))
throw new Exception("MD5 auth. error");
String dec = new String(Base64.decode(x.getValue().getBytes()));
int ind = dec.indexOf("nonce=\"")+7;
String nonce = dec.substring(ind, dec.indexOf("\"", ind+1));
String cnonce = "00deadbeef00";
writeToAir("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"+generateAuthResponse(user, pass, domain, "xmpp/"+domain, nonce, cnonce)+"</response>");
log.addMessage("Waiting for response");
x = readStanza();
// System.out.println(x);
if(x.getName().equals("failure")) {
throw new Exception("MD5 auth. error");
}
writeToAir("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
log.addMessage("Next authorization step");
x = readStanza();
if(x.getName().equals("failure"))
throw new Exception("MD5 authorization error");
} else {
if(x.child("mechanisms").hasValueOfChild("X-GOOGLE-TOKEN") && log.getProfile().getIsGoogle()>0) {
//X-GOOGLE-TOKEN authorization doing. User can disable google features using by deselecting corresponding checkbox in profile
setGoogle(true);
String resp = token;
writeToAir("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"X-GOOGLE-TOKEN\">"+resp+"</auth>");
log.addMessage("Starting google authorization");
x = readStanza();
if(x.getName().equals("failure"))
throw new Exception("GOOGLE authorization error");
} else {
//PLAIN authorization supported by GTalk server in SSL mode
log("Using plain authorization");
String resp = "\0"+user+"\0"+pass;
writeToAir("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"PLAIN\">"+MD5.toBase64(resp.getBytes())+"</auth>");
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("<iq type=\"set\" id=\"bind\">" +
"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">" +
"<resource>"+resource+"</resource></bind></iq>");
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\">" +
"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>");
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 = "<presence><show>"+show+"</show><status>"+log.getProfile().getStatus()+"</status></presence>";
} else {
//Very hard :(
//First, insert into response current status and status text
outp = "<iq type=\"set\" to=\""+log.getProfile().getUser()+"\">" +
"<query xmlns=\"google:shared-status\"><status>"+log.getProfile().getStatus()+
"</status><show>"+show+"</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+="<status>"+log.getRoster().getOnlines().elementAt(i).toString()+"</status>";
if(log.getRoster().getOnlines().elementAt(i).toString().equals(log.getProfile().getStatus()) &&
log.getRoster().getProfile().getStatusID()==0)
found = true;
}
if(!found && log.getProfile().getStatusID()==0 && !log.getProfile().getStatus().trim().equals(""))
s = "<status>"+log.getProfile().getStatus()+"</status>" +s;
outp += "<status-list show=\"default\">"+s+"</status-list>";
s = "";
found = false;
for(int i=0; i<log.getRoster().getBusies().size(); i++) {
s+="<status>"+log.getRoster().getBusies().elementAt(i).toString()+"</status>";
if(log.getRoster().getBusies().elementAt(i).toString().equals(log.getRoster().getProfile().getStatus()) &&
log.getProfile().getStatusID()==3)
found = true;
}
if(!found && log.getProfile().getStatusID()==3 && !log.getProfile().getStatus().trim().equals(""))
s = "<status>"+log.getProfile().getStatus()+"</status>" +s;
outp += "<status-list show=\"dnd\">"+s+"</status-list>";
s = "";
found = false;
for(int i=0; i<log.getRoster().getAways().size(); i++) {
s+="<status>"+log.getRoster().getAways().elementAt(i).toString()+"</status>";
if(log.getRoster().getAways().elementAt(i).toString().equals(log.getProfile().getStatus()) &&
log.getProfile().getStatusID()==1)
found = true;
}
if(!found && log.getProfile().getStatusID()==1 && !log.getProfile().getStatus().trim().equals(""))
s = "<status>"+log.getProfile().getStatus()+"</status>" +s;
outp += "<status-list show=\"away\">"+s+"</status-list>";
outp+="</query></iq>";
}
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;
}
}