/*
* JabberXml.java
*
* Created on 12 Июль 2008 г., 19:51
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
// #sijapp cond.if protocols_JABBER is "true" #
package protocol.xmpp;
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import jimm.chat.message.*;
import jimm.search.*;
import jimm.*;
import jimm.comm.*;
import jimm.modules.*;
import jimm.util.JLocale;
import protocol.*;
import protocol.net.ClientConnection;
import protocol.ui.StatusInfo;
import protocol.ui.StatusView;
import protocol.ui.XStatusInfo;
/**
*
* @author Vladimir Krukov
*/
public final class XmppConnection extends ClientConnection {
private Socket socket;
private Xmpp protocol;
private String fullJid_;
private String domain_ = "";
private String resource;
private final boolean xep0048 = false;
private byte[] pingPacket = new byte[]{' '};
private byte[] forPongPacket = StringUtils.stringToByteArrayUtf8(
"<iq type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
private String verHash = "";
private String featureList = "";
private final Vector<Object> packets = new Vector<Object>();
private boolean isGTalk_ = false;
private boolean authorized_ = false;
private boolean rosterLoaded = false;
private UserInfo singleUserInfo;
private String autoSubscribeDomain;
private XmppForm jabberForm;
// #sijapp cond.if modules_FILES is "true"#
private IBBFileTransfer ibb;
// #sijapp cond.end#
private ServiceDiscovery serviceDiscovery = null;
private AdHoc adhoc;
private SASL_ScramSha1 scramSHA1;
private static final String[] supportedEventTypes = Util.explode("mood|activity|tune", '|');
private static final String[] statusCodes = {
"u" + "navailable",
"", // online
"a" + "way",
"c" + "h" + "a" + "t",
"",
"",
"",
"",
"",
"x" + "a",
"",
"d" + "nd",
"",
""
};
private static final String S_TEXT = "te" + "xt";
private static final String S_FROM = "fr" + "om";
private static final String S_TYPE = "ty" + "pe";
private static final String S_ERROR = "e" + "rror";
private static final String S_NONE = "n" + "o" + "ne";
private static final String S_NODE = "n" + "o" + "de";
private static final String S_NICK = "ni" + "ck";
private static final String S_SET = "s" + "e" + "t";
private static final String S_REMOVE = "r" + "emove";
private static final String S_RESULT = "r" + "esult";
private static final String S_GROUP = "g" + "roup";
private static final String S_ITEM = "i" + "tem";
private static final String S_ITEMS = "i" + "tems";
private static final String S_TRUE = "t" + "rue";
private static final String S_FALSE = "fa" + "lse";
private static final String S_GET = "g" + "e" + "t";
private static final String S_TIME = "t" + "ime";
private static final String S_TITLE = "t" + "itle";
private static final String S_CODE = "c" + "ode";
private static final String S_QUERY = "que" + "ry";
private static final String S_STATUS = "st" + "atus";
private static final String S_VCARD = "vCard";
private static final String S_SUBJECT = "subje" + "ct";
private static final String S_BODY = "b" + "ody";
private static final String S_URL = "u" + "r" + "l";
private static final String S_DESC = "d" + "es"+ "c";
private static final String S_COMPOSING = "c" + "omposing";
private static final String S_ACTIVE = "ac" + "tive";
private static final String S_PAUSED = "p" + "aused";
private static final String S_CHAT = "c" + "hat";
private static final String S_GROUPCHAT = "groupc" + "hat";
private static final String S_HEADLINE = "h" + "eadline";
/**
* Get roster request
*/
private static final String GET_ROSTER_XML = "<iq type='get' id='roster'>"
+ "<query xmlns='jabber:iq:roster'/>"
+ "</iq>";
private static final byte IQ_TYPE_RESULT = 0;
private static final byte IQ_TYPE_GET = 1;
private static final byte IQ_TYPE_SET = 2;
private static final byte IQ_TYPE_ERROR = 3;
private byte nativeStatus2StatusIndex(String rawStatus) {
rawStatus = StringUtils.notNull(rawStatus);
for (byte i = 0; i < statusCodes.length; ++i) {
if (rawStatus.equals(statusCodes[i])) {
return i;
}
}
return StatusInfo.STATUS_ONLINE;
}
private String getNativeStatus(byte statusIndex) {
return statusCodes[statusIndex];
}
static boolean isTrue(String val) {
return S_TRUE.equals(val) || "1".equals(val);
}
public String getCaps() {
String caps = "http://jimm.net.ru/caps";
// #sijapp cond.if modules_ANDROID is "true" #
caps += "#android";
// #sijapp cond.end #
return "<c xmlns='http://jabber.org/protocol/caps'"
+ " node='" + caps + "' ver='"
+ Util.xmlEscape(verHash)
+ "' hash='md5'/>";
}
public XmppConnection() {
}
public void setJabber(Xmpp xmpp) {
protocol = xmpp;
resource = xmpp.getResource();
fullJid_ = xmpp.getUserId() + '/' + resource;
domain_ = Jid.getDomain(fullJid_);
}
private void setProgress(int percent) {
getJabber().setConnectingProgress(percent);
}
// #sijapp cond.if modules_ZLIB is "true" #
private void setStreamCompression() throws JimmException {
setProgress(20);
socket.startCompression();
write(getOpenStreamXml(domain_));
// #sijapp cond.if modules_DEBUGLOG is "true" #
//jimm.modules.DebugLog.println("zlib turn on");
// #sijapp cond.end #
readXmlNode(true); // "stream:stream"
parseAuth(readXmlNode(true));
}
private void setStreamTls() throws JimmException {
setProgress(15);
socket.startTls(domain_);
write(getOpenStreamXml(domain_));
// #sijapp cond.if modules_DEBUGLOG is "true" #
jimm.modules.DebugLog.println("tls turn on");
// #sijapp cond.end #
readXmlNode(true); // "stream:stream"
parseAuth(readXmlNode(true));
}
// #sijapp cond.end #
public Xmpp getJabber() {
return protocol;
}
/////////////////////////////////////////////////////
private void write(byte[] data) throws JimmException {
socket.write(data);
}
private void connectTo(String url) throws JimmException {
socket = new Socket();
socket.connectTo(url);
}
public final void disconnect() {
connect = false;
protocol = null;
}
protected final void ping() throws JimmException {
write(pingPacket);
}
protected final void pingForPong() throws JimmException {
write(forPongPacket);
}
private void putPacketIntoQueue(Object packet) {
synchronized (packets) {
packets.addElement(packet);
}
}
private boolean hasOutPackets() {
synchronized (packets) {
return !packets.isEmpty();
}
}
private void sendPacket() throws JimmException {
String packet;
synchronized (packets) {
packet = (String)packets.elementAt(0);
packets.removeElementAt(0);
}
writePacket(packet);
}
protected final boolean processPacket() throws JimmException {
if (hasOutPackets()) {
sendPacket();
return true;
}
if (processInPacket()) {
updateTimeout();
return true;
}
return false;
}
protected final void closeSocket() {
try {
write("<presence type='unavailable'><status>Logged out</status></presence>");
} catch (Exception ignored) {
}
socket.close();
socket = null;
}
protected Protocol getProtocol() {
return protocol;
}
/////////////////////////////////////////////////////
private void write(String xml) throws JimmException {
// #sijapp cond.if modules_DEBUGLOG is "true" #
//DebugLog.systemPrintln("[OUT]:\n" + xml);
// #sijapp cond.end #
write(StringUtils.stringToByteArrayUtf8(xml));
}
private void writePacket(String packet) throws JimmException {
write(packet);
}
private XmlNode readXmlNode(boolean notEmpty) throws JimmException {
// #sijapp cond.if modules_DEBUGLOG is "true" #
//DebugLog.systemPrintln("[IN]:\n" + x.toString());
// #sijapp cond.end #
return socket.readNode(notEmpty);
}
// -----------------------------------------------------------------------
private void sendRequest(String request) throws JimmException {
write(request);
}
// -----------------------------------------------------------------------
private void setAuthStatus(boolean authorized) throws JimmException {
if (!authorized_) {
authorized_ = authorized;
if (!authorized) {
getJabber().setPassword(null);
throw new JimmException(111, 0);
}
}
}
XmlNode newAccountConnect(String domain, String server) throws JimmException {
domain = Util.xmlEscape(domain);
connectTo(server);
write(getOpenStreamXml(domain));
readXmlNode(true); // "stream:stream"
XmlNode features = readXmlNode(true); // "stream:features"
if (!features.contains("regis" + "ter")) {
return null;
}
write("<iq type='get' to='" + domain
+ "' id='1'><query xmlns='jabber:iq:register'/></iq>");
return readXmlNode(true);
}
XmlNode newAccountRegister(String xml) throws JimmException {
write(xml);
XmlNode x = readXmlNode(true);
socket.close();
return x;
}
private String getSocketUrl(String server) {
String defaultServer = getJabber().getDefaultServer(domain_);
String[] url = Util.explode(server, ':');
String[] socketUrl = new String[3];
final String S_SOCKET = "s"+"ocket";
final String S_SSL = "ss"+"l";
final String S_5222 = "5222";
if (3 == url.length) {
socketUrl[0] = url[0];
socketUrl[1] = url[1];
socketUrl[2] = url[2];
} else if (2 == url.length) {
socketUrl[0] = url[1].equals(S_5222) ? S_SOCKET : S_SSL;
socketUrl[1] = url[0];
socketUrl[2] = url[1];
} else if (1 == url.length) {
socketUrl[0] = S_SOCKET;
socketUrl[1] = url[0];
socketUrl[2] = S_5222;
}
if (null != defaultServer) {
socketUrl[1] = defaultServer;
url = Util.explode(defaultServer, ':');
if (3 == url.length) {
socketUrl = url;
}
}
return socketUrl[0] + "://" + socketUrl[1] + ":" + socketUrl[2];
}
protected final void connect() throws JimmException {
connect = true;
initFeatures();
protocol.net.SrvResolver resolver = new protocol.net.SrvResolver();
String server = Config.getConfigValue(domain_, "/jabber-servers.txt");
String defaultServer = getJabber().getDefaultServer(domain_);
if (StringUtils.isEmpty(server) && (null == defaultServer)) {
server = resolver.getXmpp(domain_);
}
if (StringUtils.isEmpty(server)) {
server = domain_;
}
connectTo(getSocketUrl(server));
write(getOpenStreamXml(domain_));
setProgress(10);
readXmlNode(true); // "stream:stream"
resolver.close();
parseAuth(readXmlNode(true));
while (!authorized_) {
loginParse(readXmlNode(true));
}
setProgress(50);
socket.start();
write(GET_ROSTER_XML);
usePong();
}
private boolean processInPacket() throws JimmException {
XmlNode x = null;
try {
x = readXmlNode(false);
if (null == x) {
return false;
}
parse(x);
x = null;
} catch (JimmException e) {
throw e;
} catch (Exception e) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.panic("Jabber parse", e);
if (null != x) {
DebugLog.println("xml: " + x.toString());
}
// #sijapp cond.end #
}
return true;
}
// -----------------------------------------------------------------------
/**
* Parse inbound xml for authentication
*
* @param x Received xml
*/
private void parseAuth(XmlNode x) throws JimmException {
if ((null == x) || !x.is("stream:features")) {
nonSaslLogin();
} else {
loginParse(x);
}
}
private void nonSaslLogin() throws JimmException {
String user = Jid.getNick(protocol.getUserId());
sendRequest(
"<iq type='set' to='" + domain_ + "' id='login'>"
+ "<query xmlns='jabber:iq:auth'>"
+ "<username>" + Util.xmlEscape(user) + "</username>"
+ "<password>" + Util.xmlEscape(protocol.getPassword()) + "</password>"
+ "<resource>"+ Util.xmlEscape(resource) + "</resource>"
+ "</query>"
+ "</iq>");
XmlNode answer = readXmlNode(true);
setAuthStatus(S_RESULT.equals(answer.getAttribute(S_TYPE)));
}
private void loginParse(XmlNode x) throws JimmException {
if (x.is("stream:features")) {
parseStreamFeatures(x);
return;
// #sijapp cond.if modules_ZLIB is "true" #
} else if (x.is("compressed")) {
setStreamCompression();
return;
// #sijapp cond.end #
// #sijapp cond.if modules_ZLIB is "true" #
} else if (x.is("proceed")) {
setStreamTls();
return;
// #sijapp cond.end #
/* Reply to DIGEST-MD5 challenges */
} else if (x.is("challenge")) {
parseChallenge(x);
return;
} else if (x.is("failure")) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Failed");
// #sijapp cond.end #
setAuthStatus(false);
return;
} else if (x.is("success")) {
if (null != scramSHA1) {
if (!scramSHA1.success(new String(Util.base64decode(x.value)))) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("Server answer not valid");
// #sijapp cond.end #
setAuthStatus(false);
return;
}
scramSHA1 = null;
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Auth success");
DebugLog.systemPrintln("auth " + authorized_);
// #sijapp cond.end #
sendRequest(getOpenStreamXml(domain_));
return;
} else if (x.is("iq")) {
XmlNode iqQuery = x.childAt(0);
String id = x.getId();
if ("sess".equals(id)) {
setAuthStatus(true);
return;
}
if (null == iqQuery) {
return;
}
String queryName = iqQuery.name;
// non sasl login
if (IQ_TYPE_ERROR == getIqType(x)) {
if ("jabber:iq:auth".equals(iqQuery.getXmlns())) {
setAuthStatus(false);
}
}
if ("bind".equals(queryName)) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Send open session request");
// #sijapp cond.end #
fullJid_ = iqQuery.getFirstNodeValue(XmlNode.S_JID);
sendRequest("<iq type='set' id='sess'>"
+ "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
+ "</iq>");
return;
}
}
parse(x);
}
/**
* Parse inbound xml and execute apropriate action
*
* @param x Received xml
*/
private void parse(XmlNode x) throws JimmException {
if (x.is("iq")) {
parseIq(x);
} else if (x.is("presence")) {
parsePresence(x);
} else if (x.is("m" + "essage")) {
parseMessage(x);
} else if (x.is("stream:error")) {
setAuthStatus(false);
// #sijapp cond.if modules_DEBUGLOG is "true" #
XmlNode err = (null == x.childAt(0)) ? x : x.childAt(0);
DebugLog.systemPrintln("[INFO-JABBER] Stream error!: " + err.name + "," + err.value);
// #sijapp cond.end #
}
}
private String generateId(String key) {
return key + Util.uniqueValue();
}
private String generateId() {
return "jimm" + Util.uniqueValue();
}
private boolean isNoAutorized(String subscription) {
return S_NONE.equals(subscription) || S_FROM.equals(subscription);
}
private byte getIqType(XmlNode iq) {
String iqType = iq.getAttribute(S_TYPE);
if (S_RESULT.equals(iqType)) {
return IQ_TYPE_RESULT;
}
if (S_GET.equals(iqType)) {
return IQ_TYPE_GET;
}
if (S_SET.equals(iqType)) {
return IQ_TYPE_SET;
}
return IQ_TYPE_ERROR;
}
private void parseIqError(XmlNode iqNode, String from) throws JimmException {
XmlNode errorNode = iqNode.getFirstNode(S_ERROR);
iqNode.removeNode(S_ERROR);
// #sijapp cond.if modules_DEBUGLOG is "true" #
if (null == errorNode) {
DebugLog.println("Error without description is stupid");
} else {
DebugLog.systemPrintln(
"[INFO-JABBER] <IQ> error received: " +
"Code=" + errorNode.getAttribute(S_CODE) + " " +
"Value=" + getError(errorNode));
}
// #sijapp cond.end #
XmlNode query = iqNode.childAt(0);
if (null == query) {
// some bad happend
} else if (S_VCARD.equals(query.name)) {
loadVCard(null, from);
} else if (S_QUERY.equals(query.name)) {
String xmlns = query.getXmlns();
if ("jabber:iq:register".equals(xmlns) && (null != jabberForm)) {
jabberForm.error(getError(errorNode));
jabberForm = null;
// } else if ("jabber:iq:roster".equals(xmlns)) {
// //FIXME: stop loading if roster service was down.
} else if ("http://jabber.org/protocol/disco#items".equals(xmlns)) {
ServiceDiscovery disco = serviceDiscovery;
if (null != disco) {
serviceDiscovery = null;
disco.setError(getError(errorNode));
}
AdHoc commands = adhoc;
if ((null != commands) && commands.getJid().equals(from)) {
adhoc = null;
commands.addItems(null);
}
}
}
}
private void sendIqError(String query, String xmlns, String from, String id) {
putPacketIntoQueue("<iq type='error' to='"
+ Util.xmlEscape(from) + "' id='" + Util.xmlEscape(id) + "'>"
+ "<" + query + " xmlns='" + Util.xmlEscape(xmlns) + "'/>"
+ "<error type='cancel'>"
+ "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
+ "</error>"
+ "</iq>");
}
private boolean isMy(String from) {
return StringUtils.isEmpty(from) || getJabber().getUserId().equals(Jid.getBareJid(from));
}
private void processEmptyId(String id, byte iqType, String from) {
if (IQ_TYPE_RESULT != iqType) {
return;
}
if (id.startsWith(S_VCARD)) {
loadVCard(null, from);
}
if ((null != jabberForm) && jabberForm.getId().equals(id)) {
jabberForm.success();
jabberForm = null;
}
}
/**
* Parse the <<lit>iq</lit>> node and launch apropriate action
*
* @param iq {@link XmlNode} to parse
*/
private void parseIq(XmlNode iq) throws JimmException {
String from = StringUtils.notNull(iq.getAttribute(S_FROM));
byte iqType = getIqType(iq);
String id = iq.getId();
if (StringUtils.isEmpty(id)) {
id = generateId();
}
// #sijapp cond.if modules_FILES is "true"#
if (null != ibb) {
boolean processed = processIbb(iq, iqType, id);
if (processed) {
return;
}
}
// #sijapp cond.end#
if (IQ_TYPE_ERROR == iqType) {
parseIqError(iq, from);
return;
}
XmlNode iqQuery = iq.childAt(0);
if (null == iqQuery) {
processEmptyId(id, iqType, from);
return;
}
String queryName = iqQuery.name;
Xmpp xmpp = getJabber();
if (S_QUERY.equals(queryName)) {
String xmlns = iqQuery.getXmlns();
if ("jabber:iq:roster".equals(xmlns)) {
if (!isMy(from)) {
return;
}
if ((IQ_TYPE_RESULT == iqType) && !rosterLoaded) {
rosterLoaded = true;
TemporaryRoster roster = new TemporaryRoster(xmpp);
xmpp.setContactListStub();
while (0 < iqQuery.childrenCount()) {
XmlNode itemNode = iqQuery.popChildNode();
String jid = itemNode.getAttribute(XmlNode.S_JID);
Contact contact = roster.makeContact(jid);
contact.setName(itemNode.getAttribute(XmlNode.S_NAME));
String groupName = itemNode.getFirstNodeValue(S_GROUP);
if (StringUtils.isEmpty(groupName) || Jid.isConference(jid)) {
groupName = contact.getDefaultGroupName();
}
contact.setGroup(roster.getOrCreateGroup(groupName));
String subscription = itemNode.getAttribute("subsc" + "ription");
contact.setBooleanValue(Contact.CONTACT_NO_AUTH, isNoAutorized(subscription));
roster.addContact(contact);
}
if (!isConnected()) {
return;
}
xmpp.setContactList(roster.getGroups(), roster.mergeContacts());
Contact selfContact = xmpp.getItemByUID(xmpp.getUserId());
if (null != selfContact) {
selfContact.setBooleanValue(Contact.CONTACT_NO_AUTH, false);
xmpp.ui_updateContact(selfContact);
}
xmpp.s_updateOnlineStatus();
// #sijapp cond.if modules_XSTATUSES is "true" #
String xcode = Xmpp.xStatus.getCode(xmpp.getProfile().xstatusIndex);
if ((null != xcode) && !xcode.startsWith(XmppXStatus.XSTATUS_START)) {
setXStatus();
}
// #sijapp cond.end #
getBookmarks();
setProgress(100);
} else if (IQ_TYPE_SET == iqType) {
while (0 < iqQuery.childrenCount()) {
XmlNode itemNode = (XmlNode)iqQuery.popChildNode();
String subscription = itemNode.getAttribute("subsc" + "ription");
String jid = itemNode.getAttribute(XmlNode.S_JID);
if (Jid.isConference(jid)) {
// do nothing
} else if ((S_REMOVE).equals(subscription)) {
xmpp.removeLocalContact(xmpp.getItemByUID(jid));
} else {
String name = itemNode.getAttribute(XmlNode.S_NAME);
Contact contact = xmpp.createTempContact(jid);
String group = itemNode.getFirstNodeValue(S_GROUP);
if (StringUtils.isEmpty(group) || Jid.isConference(contact.getUserId())) {
group = contact.getDefaultGroupName();
}
contact.setName(name);
contact.setGroup(xmpp.getOrCreateGroup(group));
contact.setTempFlag(false);
contact.setBooleanValue(Contact.CONTACT_NO_AUTH, isNoAutorized(subscription));
xmpp.addLocalContact(contact);
}
}
Contact selfContact = xmpp.getItemByUID(xmpp.getUserId());
if (null != selfContact) {
selfContact.setBooleanValue(Contact.CONTACT_NO_AUTH, false);
xmpp.ui_updateContact(selfContact);
}
}
return;
} else if ("http://jabber.org/protocol/disco#info".equals(xmlns)) {
if (IQ_TYPE_GET == iqType) {
StringBuilder sb = new StringBuilder();
sb.append("<iq type='result' to='")
.append(Util.xmlEscape(from))
.append("' id='").append(Util.xmlEscape(id)).append("'>");
sb.append("<query xmlns='http://jabber.org/protocol/disco#info'>");
sb.append(featureList);
sb.append("</query></iq>");
write(sb.toString());
return;
}
if (IQ_TYPE_RESULT != iqType) {
return;
}
String name = iqQuery.getFirstNodeAttribute("identity", XmlNode.S_NAME);
xmpp.setConferenceInfo(from, name);
return;
} else if ("http://jabber.org/protocol/disco#items".equals(xmlns)) {
if (IQ_TYPE_GET == iqType) {
sendIqError(S_QUERY, xmlns, from, id);
return;
}
ServiceDiscovery disco = serviceDiscovery;
if (null != disco) {
serviceDiscovery = null;
disco.setTotalCount(iqQuery.childrenCount());
while (0 < iqQuery.childrenCount()) {
XmlNode item = iqQuery.popChildNode();
String name = item.getAttribute(XmlNode.S_NAME);
String jid = item.getAttribute(XmlNode.S_JID);
disco.addItem(name, jid);
}
disco.update();
return;
}
AdHoc commands = adhoc;
if ((null != commands) && commands.getJid().equals(from)) {
adhoc = null;
commands.addItems(iqQuery);
}
return;
} else if ("jabber:iq:register".equals(xmlns)) {
if ((null != jabberForm) && jabberForm.getId().equals(id)) {
if (jabberForm.isWaiting()) {
jabberForm.loadFromXml(iqQuery, iqQuery);
jabberForm = null;
} else {
processEmptyId(id, iqType, from);
}
}
return;
} else if ("jabber:iq:private".equals(xmlns)) {
if (!isMy(from)) {
return;
}
XmlNode storage = iqQuery.getFirstNode("sto" + "rage", "storage:bookmarks");
if (null != storage) {
loadBookmarks(storage);
}
return;
} else if ("jabber:iq:version".equals(xmlns)) {
// #sijapp cond.if modules_CLIENTS is "true" #
if (IQ_TYPE_RESULT == iqType) {
String name = iqQuery.getFirstNodeValue(XmlNode.S_NAME);
String ver = iqQuery.getFirstNodeValue("v" + "ersion");
String os = iqQuery.getFirstNodeValue("o" + "s");
name = Util.notUrls(name);
ver = Util.notUrls(ver);
os = Util.notUrls(os);
String jid = Jid.isConference(from) ? from : Jid.getBareJid(from);
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.println("ver " + jid + " " + name + " " + ver + " in " + os);
// #sijapp cond.end #
StatusView sv = Jimm.getJimm().getStatusView();
Contact c = sv.getContact();
// TODO: check protocol instance
if ((null != c) && c.getUserId().equals(jid)) {
sv.setClientVersion(name + " " + ver + " " + os);
getJabber().updateStatusView(sv, c);
}
return;
}
// #sijapp cond.end #
if (IQ_TYPE_GET == iqType) {
putPacketIntoQueue("<iq type='result' to='"
+ Util.xmlEscape(from) + "' id='" + Util.xmlEscape(id) + "'>"
+ "<query xmlns='jabber:iq:version'><name>Jimm</name><version>"
+ Util.xmlEscape(jimm.Jimm.getJimm().VERSION + " (###DATE###)")
+ "</version><os>"
+ Util.xmlEscape(Jimm.getJimm().phone.microeditionPlatform)
+ "</os></query></iq>");
// #sijapp cond.if modules_MAGIC_EYE is "true" #
String jid = Jid.isConference(from) ? from : Jid.getBareJid(from);
MagicEye.addAction(xmpp, jid, "get_version");
// #sijapp cond.end #
}
return;
} else if ("jabber:iq:last".equals(xmlns)) {
if (IQ_TYPE_GET == iqType) {
// #sijapp cond.if modules_MAGIC_EYE is "true" #
String jid = Jid.isConference(from) ? from : Jid.getBareJid(from);
MagicEye.addAction(xmpp, jid, "last_activity_request");
// #sijapp cond.end #
long time = Jimm.getCurrentGmtTime() - xmpp.getLastStatusChangeTime();
putPacketIntoQueue("<iq type='result' to='" + Util.xmlEscape(from)
+ "' id='" + Util.xmlEscape(id) + "'>"
+ "<query xmlns='jabber:iq:last' seconds='"
+ time
+ "'/></iq>");
}
return;
} else if ("http://jabber.org/protocol/muc#owner".equals(xmlns)) {
if (IQ_TYPE_RESULT == iqType) {
if (null != jabberForm) {
jabberForm.loadFromXml(iqQuery, iq);
jabberForm = null;
}
}
}
} else if (S_TIME.equals(queryName)) {
if (IQ_TYPE_GET != iqType) {
return;
}
// #sijapp cond.if modules_MAGIC_EYE is "true" #
String jid = Jid.isConference(from) ? from : Jid.getBareJid(from);
MagicEye.addAction(xmpp, jid, "get_time");
// #sijapp cond.end #
int gmtOffset = Options.getInt(Options.OPTION_GMT_OFFSET);
putPacketIntoQueue("<iq type='result' to='" + Util.xmlEscape(from)
+ "' id='" + Util.xmlEscape(id) + "'>"
+ "<time xmlns='urn:xmpp:time'><tzo>"
+ (0 <= gmtOffset ? "+" : "-") + Util.makeTwo(Math.abs(gmtOffset)) + ":00"
+ "</tzo><utc>"
+ Util.getUtcDateString(Jimm.getCurrentGmtTime())
+ "</utc></time></iq>");
return;
} else if (("p" + "ing").equals(queryName)) {
writePacket("<iq to='" + Util.xmlEscape(from) + "' id='" + Util.xmlEscape(id) + "' type='result'/>");
return;
} else if (("pu" + "bsub").equals(queryName)) {
if (!isMy(from)) {
return;
}
loadBookmarks(iqQuery.getFirstNodeRecursive("sto" + "rage"));
return;
} else if (S_VCARD.equals(queryName)) {
if (IQ_TYPE_RESULT == iqType) {
loadVCard(iqQuery, from);
}
return;
} else if ("x".equals(queryName)) {
String xmlns = iqQuery.getXmlns();
if ("http://jabber.org/protocol/rosterx".equals(xmlns)) {
if (Jid.isGate(from)) {
Contact c = getJabber().getItemByUID(from);
if ((null != c) && !c.isTemp() && c.isAuth()) {
putPacketIntoQueue("<iq type='result' to='"
+ Util.xmlEscape(from) + "' id='" + Util.xmlEscape(id) + "' />");
parseRosterExchange(iqQuery, '@' + from);
return;
}
}
}
} else if (("c" + "ommand").equals(queryName)) {
if (null != adhoc) {
adhoc.loadCommandXml(iq, id);
}
}
if (IQ_TYPE_GET == iqType) {
sendIqError(iqQuery.name, iqQuery.getXmlns(), from, id);
}
}
public void saveVCard(UserInfo userInfo) {
if (null == userInfo.vCard) {
userInfo.vCard = XmlNode.getEmptyVCard();
}
userInfo.vCard.removeBadVCardTags("TEL");
userInfo.vCard.removeBadVCardTags("EMAIL");
userInfo.vCard.setValue("NICKNAME", userInfo.nick);
userInfo.vCard.setValue("BDAY", userInfo.birthDay);
userInfo.vCard.setValue("URL", userInfo.homePage);
userInfo.vCard.setValue("FN", userInfo.getName());
userInfo.vCard.setValue("N", null, "GIVEN", userInfo.firstName);
userInfo.vCard.setValue("N", null, "FAMILY", userInfo.lastName);
userInfo.vCard.setValue("N", null, "MIDDLE", "");
userInfo.vCard.setValue("EMAIL", new String[]{"INTERNET"}, "USERID", userInfo.email);
userInfo.vCard.setValue("TEL", new String[]{"HOME", "VOICE"}, "NUMBER", userInfo.cellPhone);
userInfo.vCard.setValue("ADR", new String[]{"HOME"}, "STREET", userInfo.homeAddress);
userInfo.vCard.setValue("ADR", new String[]{"HOME"}, "LOCALITY", userInfo.homeCity);
userInfo.vCard.setValue("ADR", new String[]{"HOME"}, "REGION", userInfo.homeState);
userInfo.vCard.setValue("TEL", new String[]{}, "NUMBER", "");
userInfo.vCard.setValue("TEL", new String[]{"WORK", "VOICE"}, "NUMBER", userInfo.workPhone);
userInfo.vCard.setValue("ORG", null, "ORGNAME", userInfo.workCompany);
userInfo.vCard.setValue("ORG", null, "ORGUNIT", userInfo.workDepartment);
userInfo.vCard.setValue("TITLE", userInfo.workPosition);
userInfo.vCard.setValue("DESC", userInfo.about);
userInfo.vCard.cleanXmlTree();
StringBuilder packet = new StringBuilder();
packet.append("<iq type='set' id='").append(generateId()).append("'>");
userInfo.vCard.toString(packet);
packet.append("</iq>");
putPacketIntoQueue(packet.toString());
}
private void loadVCard(XmlNode vCard, String from) {
UserInfo userInfo = singleUserInfo;
if ((null == userInfo) || !from.equals(userInfo.realUin)) {
return;
}
userInfo.auth = false;
userInfo.uin = from;
if (Jid.isConference(from)) {
Contact c = getJabber().getItemByUID(Jid.getBareJid(from));
if (c instanceof XmppServiceContact) {
XmppContact.SubContact sc = ((XmppServiceContact)c).getExistSubContact(Jid.getResource(from, null));
if ((null != sc) && (null != sc.realJid)) {
userInfo.uin = sc.realJid;
}
}
}
if (null == vCard) {
userInfo.updateProfileView();
singleUserInfo = null;
return;
}
String name[] = new String[3];
name[0] = vCard.getFirstNodeValue("N", "GIVEN");
name[1] = vCard.getFirstNodeValue("N", "MIDDLE");
name[2] = vCard.getFirstNodeValue("N", "FAMILY");
if (StringUtils.isEmpty(Util.implode(name, ""))) {
userInfo.firstName = vCard.getFirstNodeValue("FN");
userInfo.lastName = null;
} else {
userInfo.lastName = name[2];
name[2] = null;
userInfo.firstName = Util.implode(name, " ");
}
userInfo.nick = vCard.getFirstNodeValue("NICKNAME");
userInfo.birthDay = vCard.getFirstNodeValue("BDAY");
userInfo.email = vCard.getFirstNodeValue("EMAIL", new String[]{"INTERNET"}, "USERID", true);
userInfo.about = vCard.getFirstNodeValue("DESC");
userInfo.homePage = vCard.getFirstNodeValue("URL");
userInfo.homeAddress = vCard.getFirstNodeValue("ADR", new String[]{"HOME"}, "STREET", true);
userInfo.homeCity = vCard.getFirstNodeValue("ADR", new String[]{"HOME"}, "LOCALITY", true);
userInfo.homeState = vCard.getFirstNodeValue("ADR", new String[]{"HOME"}, "REGION", true);
userInfo.cellPhone = vCard.getFirstNodeValue("TEL", new String[]{"HOME", "VOICE"}, "NUMBER", true);
userInfo.workCompany = vCard.getFirstNodeValue("ORG", null, "ORGNAME");
userInfo.workDepartment = vCard.getFirstNodeValue("ORG", null, "ORGUNIT");
userInfo.workPosition = vCard.getFirstNodeValue("TITLE");
userInfo.workPhone = vCard.getFirstNodeValue("TEL", new String[]{"WORK", "VOICE"}, "NUMBER");
if (!Jid.isGate(from)) {
userInfo.setOptimalName();
}
if (userInfo.isEditable()) {
userInfo.vCard = vCard;
}
userInfo.updateProfileView();
XmlNode bs64photo = vCard.getFirstNode("PHOTO");
bs64photo = (null == bs64photo) ? null : bs64photo.getFirstNode("BINVAL");
if (null != bs64photo) {
new Thread(new AvatarLoader(userInfo, bs64photo)).start();
}
bs64photo = null;
singleUserInfo = null;
}
private void loadBookmarks(XmlNode storage) {
if ((null == storage) || (0 == storage.childrenCount())) {
return;
}
if (!"storage:bookmarks".equals(storage.getXmlns())) {
return;
}
Xmpp xmpp = getJabber();
Group group = xmpp.getOrCreateGroup(JLocale.getString(Xmpp.CONFERENCE_GROUP));
int autoJoinCount = xmpp.isReconnect() ? 0 : 7;
Vector<Contact> contacts = xmpp.getContactItems();
while (0 < storage.childrenCount()) {
XmlNode item = storage.popChildNode();
String jid = item.getAttribute(XmlNode.S_JID);
if ((null == jid) || !Jid.isConference(jid)) {
continue;
}
String name = item.getAttribute(XmlNode.S_NAME);
String nick = item.getFirstNodeValue(S_NICK);
boolean autojoin = isTrue(item.getAttribute("au" + "tojoin"));
String password = item.getAttribute("passwor" + "d");
XmppServiceContact conference = (XmppServiceContact) xmpp.createTempContact(jid, name);
conference.setMyName(nick);
conference.setTempFlag(false);
conference.setBooleanValue(Contact.CONTACT_NO_AUTH, false);
conference.setAutoJoin(autojoin);
conference.setPassword(password);
conference.setGroup(group);
if (-1 == Util.getIndex(contacts, conference)) {
contacts.addElement(conference);
}
if (conference.isAutoJoin() && (0 < autoJoinCount)) {
xmpp.join(conference);
autoJoinCount--;
}
}
xmpp.setContactListAddition(group);
xmpp.rejoin();
}
/**
* Parse the <<lit>presence</lit>> node and launch apropriate action
*
* @param x {@link XmlNode} to parse
*/
private void parsePresence(XmlNode x) {
final String fromFull = x.getAttribute(S_FROM);
final String from = Jid.getBareJid(fromFull);
final String fromRes = Jid.getResource(fromFull, "");
String type = x.getAttribute(S_TYPE);
if (S_ERROR.equals(type)) {
XmlNode errorNode = x.getFirstNode(S_ERROR);
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln(
"[INFO-JABBER] <IQ> error received: " +
"Code=" + errorNode.getAttribute(S_CODE) + " " +
"Value=" + errorNode.getFirstNodeValue(S_TEXT));
// #sijapp cond.end #
boolean showError = Jid.isGate(from);
if (Jid.isConference(from)) {
XmppServiceContact conf = (XmppServiceContact)getJabber().getItemByUID(from);
if (null != conf) {
int code = Util.strToIntDef(errorNode.getAttribute(S_CODE), -1);
conf.nickError(getJabber(), fromRes, code, getError(errorNode));
return;
}
}
if (showError) {
getJabber().addMessage(new SystemNotice(getJabber(),
SystemNotice.TYPE_NOTICE_ERROR, from, getError(errorNode)));
}
Contact c = getJabber().getItemByUID(from);
if (null == c) {
return;
}
c.setOfflineStatus();
c.setBooleanValue(Contact.CONTACT_NO_AUTH, true);
return;
}
if (("subscr" + "ibe").equals(type)) {
if (isAutoGateContact(from)) {
sendSubscribed(from);
requestSubscribe(from);
} else {
getJabber().addMessage(new SystemNotice(getJabber(), SystemNotice.TYPE_NOTICE_AUTHREQ, from, null));
}
Contact c = getJabber().getItemByUID(from);
autoRenameContact(c, x);
autoMoveContact(c, x);
return;
}
if (("subscr" + "ibed").equals(type)) {
if (!isAutoGateContact(from)) {
getJabber().setAuthResult(from, true);
}
autoRenameContact(getJabber().getItemByUID(from), x);
return;
}
if (("unsubscr" + "ibed").equals(type)) {
getJabber().setAuthResult(from, false);
return;
}
if (null == type) {
type = x.getFirstNodeValue("sh" + "ow");
}
if (null == type) {
type = "";
}
XmppContact contact = (XmppContact)getJabber().getItemByUID(from);
if (null == contact) {
String fullJid = Jid.realJidToJimmJid(fromFull);
contact = (XmppContact)getJabber().getItemByUID(fullJid);
if (null == contact) {
return;
}
}
int priority = Util.strToIntDef(x.getFirstNodeValue("prior" + "ity"), 0);
String statusString = x.getFirstNodeValue(S_STATUS);
if (Jid.isConference(from)) {
XmlNode xMuc = x.getXNode("http://jabber.org/protocol/muc#user");
XmlNode item = (null == xMuc) ? null : xMuc.getFirstNode(S_ITEM);
XmppServiceContact conf = (XmppServiceContact)contact;
String reasone = null;
priority = 0;
if (null != item) {
String affiliation = item.getAttribute(XmlNode.S_AFFILIATION);
if (("m" + "ember").equals(affiliation)) {
priority |= XmppServiceContact.AFFILIATION_MEMBER;
} else if (("o" + "wner").equals(affiliation)) {
priority |= XmppServiceContact.AFFILIATION_OWNER;
} else if (("a" + "dmin").equals(affiliation)) {
priority |= XmppServiceContact.AFFILIATION_ADMIN;
} else {
priority |= XmppServiceContact.AFFILIATION_NONE;
}
String role = item.getAttribute(XmlNode.S_ROLE);
if (("m" + "oderator").equals(role)) {
priority |= XmppServiceContact.ROLE_MODERATOR;
} else if (("p" + "articipant").equals(role)) {
priority |= XmppServiceContact.ROLE_PARTICIPANT;
} else if (S_NONE.equals(role)) {
reasone = item.getFirstNodeValue("r" + "eason");
item = null;
} else {// "visitor"
priority |= XmppServiceContact.ROLE_VISITOR;
}
}
getJabber().setConfContactStatus(conf, fromRes,
nativeStatus2StatusIndex(type), statusString, priority);
if (null != item) {
String newNick = item.getAttribute(XmlNode.S_NICK);
if (null != newNick) {
getJabber().setConfContactStatus(conf, newNick,
nativeStatus2StatusIndex(""), "", priority);
conf.nickChainged(getJabber(), fromRes, newNick);
} else {
conf.nickOnline(getJabber(), fromRes);
}
String realJid = item.getAttribute(XmlNode.S_JID);
if (null != realJid) {
conf.setRealJid(fromRes, Jid.getBareJid(realJid));
}
// #sijapp cond.if modules_CLIENTS is "true" #
contact.setClient(fromRes, x.getFirstNodeAttribute("c", S_NODE));
// #sijapp cond.end #
} else {
int code = 0;
if (null != xMuc) {
code = Util.strToIntDef(xMuc.getFirstNodeAttribute(S_STATUS, S_CODE), 0);
}
conf.nickOffline(getJabber(), fromRes, code, reasone);
}
if (conf.getMyName().equals(fromRes)) {
getJabber().ui_changeContactStatus(conf);
}
updateConfPrivate(conf, fromRes);
} else {
// #sijapp cond.if modules_XSTATUSES is "true" #
if (!("u" + "navailable").equals(type)) {
if ((XStatusInfo.XSTATUS_NONE == contact.getXStatusIndex())
|| !Xmpp.xStatus.isPep(contact.getXStatusIndex())) {
XmlNode xNode = x.getXNode(S_FEATURE_XSTATUS);
String id = getXStatus(xNode);
String xtext = null;
if (null != id) {
xtext = xNode.getFirstNodeValue(S_TITLE);
String s = StringUtils.notNull(statusString);
if (StringUtils.isEmpty(xtext)) {
xtext = null;
} else if (s.startsWith(xtext)) {
xtext = statusString;
statusString = null;
}
}
contact.setXStatus(id, xtext);
}
if (Jid.isPyIcqGate(from)) {
setXStatusToIcqTransport((XmppServiceContact)contact);
}
}
// #sijapp cond.end #
getJabber().setContactStatus(contact, fromRes, nativeStatus2StatusIndex(type), statusString, priority);
contact.updateMainStatus(getJabber());
// #sijapp cond.if modules_CLIENTS is "true" #
if (contact.isOnline()) {
contact.setClient(fromRes, x.getFirstNodeAttribute("c", S_NODE));
}
// #sijapp cond.end #
if (contact.getUserId().equals(contact.getName())) {
getJabber().renameContact(contact, getNickFromNode(x));
}
getJabber().ui_changeContactStatus(contact);
}
}
private String getNickFromNode(XmlNode x) {
String name = x.getFirstNodeValueRecursive("n" + "ickname");
return (null == name) ? x.getFirstNodeValue(S_NICK) : name;
}
private void autoRenameContact(Contact contact, XmlNode x) {
if (null == contact) {
return;
}
String name = getNickFromNode(x);
if (null == name) {
return;
}
if (contact.getUserId().equals(contact.getName())) {
getJabber().renameContact(contact, name);
}
}
private void autoMoveContact(Contact contact, XmlNode presence) {
if (null == contact) {
return;
}
XmlNode x = presence.getXNode("http:/" + "/delx.cjb.net/protocol/roster-subsync");
if (null == x) {
return;
}
x = x.childAt(0);
if (null == x) {
return;
}
Group g = getJabber().getOrCreateGroup(x.getFirstNodeValue(S_GROUP));
String name = x.getAttribute(XmlNode.S_NAME);
boolean update = false;
if (null != g) {
contact.setGroup(g);
update = true;
}
if (StringUtils.isEmpty(name)) {
contact.setName(name);
update = true;
}
if (update) {
updateContact((XmppContact) contact);
}
}
// #sijapp cond.if modules_XSTATUSES is "true" #
private void parseEvent(XmlNode eventNode, String fullJid) {
if (null == eventNode) {
return;
}
XmppContact contact = (XmppContact)getJabber().getItemByUID(Jid.getBareJid(fullJid));
if (null == contact) {
return;
}
XmlNode statusNode = eventNode.getFirstNode(S_ITEMS);
String eventType = "";
if (null != statusNode) {
eventType = statusNode.getAttribute(S_NODE);
int start = eventType.lastIndexOf('/');
if (-1 != start) {
eventType = eventType.substring(start + 1);
}
statusNode = statusNode.getFirstNode(S_ITEM);
}
if (null != statusNode) {
statusNode = statusNode.childAt(0);
}
if (!StringUtils.contains(supportedEventTypes, eventType)) {
return;
}
if ((null == statusNode) || (0 == statusNode.childrenCount())) {
if ((XStatusInfo.XSTATUS_NONE != contact.getXStatusIndex())
&& Xmpp.xStatus.isType(contact.getXStatusIndex(), eventType)) {
contact.setXStatus("", "");
}
return;
}
String text = statusNode.getFirstNodeValue(S_TEXT);
statusNode.removeNode(S_TEXT);
StringBuilder status = new StringBuilder();
while (null != statusNode) {
status.append(':').append(statusNode.name);
statusNode = statusNode.childAt(0);
}
status.deleteCharAt(0);
if ((XStatusInfo.XSTATUS_NONE == contact.getXStatusIndex())
|| Xmpp.xStatus.isPep(contact.getXStatusIndex())) {
contact.setXStatus(status.toString(), text);
}
}
private String getXStatus(XmlNode x) {
return (null == x) ? null : (XmppXStatus.XSTATUS_START + x.getId());
}
// #sijapp cond.end #
private void parseMessageEvent(XmlNode messageEvent, String from) {
if (null == messageEvent) {
return;
}
if (messageEvent.contains("offl" + "ine")) {
// <x><offline/><id/></x>
setMessageSent(messageEvent.getFirstNodeValue(XmlNode.S_ID),
PlainMessage.NOTIFY_FROM_SERVER);
return;
}
if (messageEvent.contains("deli" + "vered")) {
setMessageSent(messageEvent.getFirstNodeValue(XmlNode.S_ID),
PlainMessage.NOTIFY_FROM_CLIENT);
return;
}
// #sijapp cond.if modules_SOUND is "true" #
if (0 < Options.getInt(Options.OPTION_TYPING_MODE)) {
getJabber().beginTyping(from, messageEvent.contains(S_COMPOSING));
}
// #sijapp cond.end #
}
// #sijapp cond.if modules_SOUND is "true" #
private void parseChatState(XmlNode message, String from) {
if (0 < Options.getInt(Options.OPTION_TYPING_MODE)) {
if (message.contains(S_ACTIVE)
|| message.contains("gon" + "e")
|| message.contains(S_PAUSED)
|| message.contains("inactiv" + "e")) {
getJabber().beginTyping(from, false);
} else if (message.contains(S_COMPOSING)) {
getJabber().beginTyping(from, true);
}
}
}
// #sijapp cond.end #
private String getDate(XmlNode message) {
XmlNode offline = message.getXNode("jabber:x:delay");
if (null == offline) {
offline = message.getFirstNode("d" + "elay");
}
return (null == offline) ? null : offline.getAttribute("stamp");
}
private void prepareFirstPrivateMessage(String jid) {
final XmppServiceContact conf =
(XmppServiceContact)getJabber().getItemByUID(Jid.getBareJid(jid));
if (null == conf) { // don't have conference
return;
}
XmppContact.SubContact sub = conf.getExistSubContact(Jid.getResource(jid, ""));
if (null == sub) { // don't have contact
return;
}
byte role = (byte)(sub.priority & XmppServiceContact.ROLE_MASK);
if (XmppServiceContact.ROLE_MODERATOR == role) {
// moderators without antispam
getJabber().addTempContact(getJabber().createTempContact(jid));
}
}
/**
* Parse the <<lit>message</lit>> node and launch apropriate action
*
* @param msg {@link XmlNode} to parse
*/
private void parseMessage(XmlNode msg) {
msg.removeNode("h" + "tml");
String type = msg.getAttribute(S_TYPE);
boolean isGroupchat = ("groupc" + "hat").equals(type);
boolean isError = S_ERROR.equals(type);
String fullJid = msg.getAttribute(S_FROM);
boolean isConference = Jid.isConference(fullJid);
if (!isGroupchat) {
fullJid = Jid.realJidToJimmJid(fullJid);
}
String from = Jid.getBareJid(fullJid);
// message resender
if (from.equals(getJabber().getUserId())) {
XmlNode addresses = msg.getFirstNode("a" + "ddresses");
if (null != addresses) {
String ofrom = null;
while (0 < addresses.childrenCount()) {
XmlNode address = addresses.popChildNode();
if ("ofrom".equals(address.getAttribute(S_TYPE))) {
ofrom = address.getAttribute(XmlNode.S_JID);
break;
}
}
if (null != ofrom) {
fullJid = ofrom;
if (!isGroupchat) {
fullJid = Jid.realJidToJimmJid(fullJid);
}
isConference = Jid.isConference(fullJid);
from = Jid.getBareJid(fullJid);
}
}
}
if (isConference && !isGroupchat) {
from = fullJid;
}
String fromRes = Jid.getResource(fullJid, null);
String subject = msg.getFirstNodeValue(S_SUBJECT);
String text = msg.getFirstNodeValue(S_BODY);
if ((null != subject) && (null == text)) {
text = "";
}
if ("jubo@nologin.ru".equals(from) && msg.contains("juick")) {
parseBlogMessage("juick@juick.com", msg, text, "JuBo");
return;
}
if (protocol.isBlogBot(from)) {
parseBlogMessage(from, msg, text, null);
return;
}
if (!isConference) {
if (msg.contains("atte" + "ntion")) {
type = S_CHAT;
text = PlainMessage.CMD_WAKEUP;
subject = null;
}
}
// #sijapp cond.if modules_SOUND is "true" #
if (!isConference || !isGroupchat) {
parseChatState(msg, from);
}
// #sijapp cond.end #
if (null == text) {
XmlNode received = msg.getFirstNode("recei" + "ved");
if (null != received) {
String id = received.getId();
if (null == id) {
id = msg.getId();
}
setMessageSent(id, PlainMessage.NOTIFY_FROM_CLIENT);
return;
}
if (!Jid.isConference(from) && !isError) {
parseMessageEvent(msg.getXNode("jabber:x:event"), from);
// #sijapp cond.if modules_XSTATUSES is "true" #
parseEvent(msg.getFirstNode("ev" + "ent"), fullJid);
// #sijapp cond.end #
}
return;
}
// #sijapp cond.if modules_DEBUGLOG isnot "true" #
msg.removeNode(S_SUBJECT);
msg.removeNode(S_BODY);
// #sijapp cond.end #
if ((null != subject) && (-1 == text.indexOf(subject))) {
text = subject + "\n\n" + text;
}
text = StringUtils.trim(text);
final XmppContact c = (XmppContact)getJabber().getItemByUID(from);
if (msg.contains(S_ERROR)) {
final String errorText = getError(msg.getFirstNode(S_ERROR));
if (null != errorText) {
text = errorText + "\n-------\n" + text;
}
} else {
if ((null != c) && msg.contains("c" + "aptcha")) {
final XmppForm form = new XmppForm(XmppForm.TYPE_CAPTCHA,
getJabber(), from);
form.showCaptcha(msg);
return;
}
final XmlNode oobNode = msg.getXNode("jabber:x:oob");
if (null != oobNode) {
String url = oobNode.getFirstNodeValue(S_URL);
if (null != url) {
text += "\n\n" + url;
String desc = oobNode.getFirstNodeValue(S_DESC);
if (null != desc) {
text += "\n" + desc;
}
msg.removeNode(S_URL);
msg.removeNode(S_DESC);
}
}
if (!isGroupchat && msg.contains("reques" + "t") && (null != msg.getId())) {
putPacketIntoQueue("<message to='" + Util.xmlEscape(fullJid)
+ "' id='" + Util.xmlEscape(msg.getId())
+ "'><received xmlns='urn:xmpp:receipts' id='"
+ Util.xmlEscape(msg.getId()) + "'/></message>");
}
if (c instanceof XmppServiceContact) {
isConference = c.isConference();
if ((null != subject) && isConference && isGroupchat) {
String prevSubject = StringUtils.notNull(c.getStatusText());
((XmppServiceContact)c).setSubject(subject);
getJabber().ui_changeContactStatus(c);
if (prevSubject.equals(subject)) {
prevSubject = null;
}
subject = null;
fromRes = null;
if (StringUtils.isEmpty(prevSubject) && !c.hasUnreadMessage() && !c.hasChat()) {
return;
}
}
}
}
if (StringUtils.isEmpty(text)) {
return;
}
text = StringUtils.convert(Jid.isMrim(from)
? StringUtils.MRIM2JIMM : StringUtils.JABBER2JIMM,
text);
final String date = getDate(msg);
final boolean isOnlineMessage = (null == date);
long time = isOnlineMessage ? Jimm.getCurrentGmtTime() : Util.createGmtDate(date);
final PlainMessage message = new PlainMessage(from, getJabber(), time, text, !isOnlineMessage);
if (null == c) {
if (isConference && !isGroupchat) {
prepareFirstPrivateMessage(from);
}
} else {
if (isConference) {
final XmppServiceContact conf = (XmppServiceContact)c;
if (isGroupchat && (null != fromRes)) {
if (isOnlineMessage && fromRes.equals(conf.getMyName())) {
if (isMessageExist(msg.getId())) {
setMessageSent(msg.getId(), PlainMessage.NOTIFY_FROM_CLIENT);
return;
}
if (Jid.isIrcConference(fullJid)) {
return;
}
}
message.setName(conf.getNick(fromRes));
}
} else {
c.setActiveResource(fromRes);
}
}
getJabber().addMessage(message, S_HEADLINE.equals(type));
}
private void parseBlogMessage(String to, XmlNode msg, String text, String botNick) {
text = StringUtils.notNull(text);
String userNick = getNickFromNode(msg);
if (null == userNick) {
userNick = msg.getFirstNodeAttribute("juick", "uname");
}
if (StringUtils.isEmpty(userNick)) {
userNick = null;
}
String nick = userNick;
if (null != botNick) {
nick = StringUtils.notNull(userNick) + "@" + botNick;
}
if (null != userNick) {
userNick += ':';
int nickPos = text.indexOf(userNick);
if (0 == nickPos) {
text = text.substring(userNick.length() + 1);
}
}
text = StringUtils.convert(StringUtils.JABBER2JIMM, text);
text = StringUtils.trim(text);
if (StringUtils.isEmpty(text)) {
return;
}
String date = getDate(msg);
long time = (null == date) ? Jimm.getCurrentGmtTime() : Util.createGmtDate(date);
PlainMessage message = new PlainMessage(to, getJabber(), time, text, false);
if (null != nick) {
message.setName(('@' == nick.charAt(0)) ? nick.substring(1) : nick);
}
getJabber().addMessage(message);
}
String getError(XmlNode errorNode) {
if (null == errorNode) {
return S_ERROR;
}
String errorText = errorNode.getFirstNodeValue(S_TEXT);
if (null == errorText) {
errorText = errorNode.value;
}
if (null == errorText) {
errorText = "error " + errorNode.getAttribute(S_CODE);
if (null != errorNode.childAt(0)) {
errorText += ": " + errorNode.childAt(0).name;
}
}
return errorText;
}
private boolean isMechanism(XmlNode list, String myMechanism) {
for (int i = 0; i < list.childrenCount(); ++i) {
XmlNode mechanism = list.childAt(i);
if (mechanism.is("mechanism") && myMechanism.equals(mechanism.value)) {
return true;
}
}
return false;
}
/**
* Parse the <<lit>stream:features</lit>> node and launch apropriate action
*
* @param x {@link XmlNode} to parse
*/
private void parseStreamFeatures(XmlNode x) throws JimmException {
XmlNode x2 = null;
if (0 == x.childrenCount()) {
nonSaslLogin();
return;
}
// #sijapp cond.if modules_ZLIB is "true" #
// #sijapp cond.if modules_ANDROID is "true" #
/* Check for tls */
x2 = x.getFirstNode("starttls");
if (null != x2) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.println("starttls");
// #sijapp cond.end #
sendRequest("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
return;
}
// #sijapp cond.end #
/* Check for stream compression method */
x2 = x.getFirstNode("compression");
if ((null != x2) && "zlib".equals(x2.getFirstNodeValue("method"))) {
sendRequest("<compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>");
return;
}
// #sijapp cond.end #
/* Check for authentication mechanisms */
x2 = x.getFirstNode("mechanisms");
if ((null != x2) && x2.contains("mechanism")) {
String auth = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' ";
String googleToken = null;
/* X-GOOGLE-TOKEN authentication */
if (isMechanism(x2, "X-GOOGLE-TOKEN")) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Using X-GOOGLE-TOKEN");
// #sijapp cond.end #
isGTalk_ = true;
googleToken = getGoogleToken(getJabber().getUserId(), getJabber().getPassword());
if (null == googleToken) {
throw new JimmException(111, 1);
}
}
/* DIGEST-MD5 authentication */
if (isMechanism(x2, "DIGEST-MD5")) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Using DIGEST-MD5");
// #sijapp cond.end #
auth += "mechanism='DIGEST-MD5'/>";
} else if (isMechanism(x2, "SCRAM-SHA-1")) {
auth += "mechanism='SCRAM-SHA-1'>";
scramSHA1 = new SASL_ScramSha1();
auth +=Util.xmlEscape(scramSHA1.init(protocol.getUserId(), protocol.getPassword()));
auth += "</auth>";
} else if (null != googleToken) {
auth += "mechanism='X-GOOGLE-TOKEN'>" + googleToken + "</auth>";
/* PLAIN authentication */
} else {
boolean canUsePlain = true;
// #sijapp cond.if modules_ANDROID is "true" #
canUsePlain = socket.isSecured();
// #sijapp cond.end #
if (canUsePlain && isMechanism(x2, "PLAIN")) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Using PLAIN");
// #sijapp cond.end #
auth += "mechanism='PLAIN'>";
OutStream data = new OutStream();
data.writeUtf8String(getJabber().getUserId());
data.writeByte(0);
data.writeUtf8String(Jid.getNick(getJabber().getUserId()));
data.writeByte(0);
data.writeUtf8String(getJabber().getPassword());
auth += MD5.toBase64(data.toByteArray());
auth += "</auth>";
} else if (canUsePlain && isGTalk_) {
nonSaslLogin();
return;
} else {
setAuthStatus(false);
return;
}
}
sendRequest(auth);
return;
}
/* Check for resource bind */
if (x.contains("bind")) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Send bind request");
// #sijapp cond.end #
sendRequest("<iq type='set' id='bind'>"
+ "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
+ "<resource>" + Util.xmlEscape(resource) + "</resource>"
+ "</bind>"
+ "</iq>");
return;
}
x2 = x.getFirstNode("a"+"uth", "http://jabber.org/features/iq-auth");
if (null != x2) {
nonSaslLogin();
return;
}
}
/**
* Parse the <<lit>challenge</lit>> node and launch apropriate action
*
* @param x {@link XmlNode} to parse
*/
private void parseChallenge(XmlNode x) throws JimmException {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Received challenge");
// #sijapp cond.end #
String resp = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'";
String challenge = MD5.decodeBase64(x.value);
if (null != scramSHA1) {
resp += ">" + scramSHA1.response(challenge) + "</response>";
} else { // md5
int nonceIndex = challenge.indexOf("nonce=");
if (nonceIndex >= 0) {
nonceIndex += 7;
String nonce = challenge.substring(nonceIndex, challenge.indexOf('\"', nonceIndex));
String cnonce = "123456789abcd";
resp += ">";
resp += responseMd5Digest(
Jid.getNick(getJabber().getUserId()),
getJabber().getPassword(),
domain_,
"xmpp/" + domain_,
nonce,
cnonce);
resp += "</response>";
} else {
resp += "/>";
}
}
sendRequest(resp);
}
/**
* This routine generates MD5-DIGEST response via SASL specification
* (From BOMBUS project)
*
* @param user user
* @param pass password
* @param realm realm
* @param digestUri digest uri
* @param nonce nonce
* @param cnonce cnonce
* @return md5 response
*/
private String responseMd5Digest(String user, String pass,
String realm, String digestUri, String nonce, String cnonce) {
MD5 hUserRealmPass = new MD5();
hUserRealmPass.init();
hUserRealmPass.updateASCII(user);
hUserRealmPass.update((byte) ':');
hUserRealmPass.updateASCII(realm);
hUserRealmPass.update((byte) ':');
hUserRealmPass.updateASCII(pass);
hUserRealmPass.finish();
MD5 hA1 = new MD5();
hA1.init();
hA1.update(hUserRealmPass.getDigestBits());
hA1.update((byte) ':');
hA1.updateASCII(nonce);
hA1.update((byte) ':');
hA1.updateASCII(cnonce);
hA1.finish();
MD5 hA2 = new MD5();
hA2.init();
hA2.updateASCII("AUTHENTICATE:");
hA2.updateASCII(digestUri);
hA2.finish();
MD5 hResp = new MD5();
hResp.init();
hResp.updateASCII(hA1.getDigestHex());
hResp.update((byte) ':');
hResp.updateASCII(nonce);
hResp.updateASCII(":00000001:");
hResp.updateASCII(cnonce);
hResp.updateASCII(":auth:");
hResp.updateASCII(hA2.getDigestHex());
hResp.finish();
String quote = "\"";
return MD5.toBase64(StringUtils.stringToByteArrayUtf8(
"username=\"" + user +
"\",realm=\"" + realm +
"\",nonce=\"" + nonce +
"\",cnonce=\"" + cnonce +
"\",nc=00000001,digest-uri=\"" + digestUri +
"\",qop=auth,response=" + quote + hResp.getDigestHex() + quote +
",charset=utf-8"));
}
/**
* Generates X-GOOGLE-TOKEN response by communication with
* http://www.google.com
* (From mGTalk project)
*
* @param jid jid
* @param password password
* @return google token
*/
private String getGoogleToken(String jid, String password) {
try {
String escapedJid = Util.urlEscape(jid);
String first = "Email=" + escapedJid
+ "&Passwd=" + Util.urlEscape(password)
+ "&PersistentCookie=false&source=googletalk";
HttpsConnection c = (HttpsConnection) Connector
.open("https:/" + "/www.google.com:443/accounts/ClientAuth?" + first);
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Connecting to www.google.com");
// #sijapp cond.end #
DataInputStream dis = c.openDataInputStream();
String str = readLine(dis);
if (str.startsWith("SID=")) {
String SID = str.substring(4, str.length());
str = readLine(dis);
String 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);
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("[INFO-JABBER] Next www.google.com connection");
// #sijapp cond.end #
dis = c.openDataInputStream();
str = readLine(dis);
OutStream data = new OutStream();
data.writeByte(0);
data.writeUtf8String(Jid.getNick(jid));
data.writeByte(0);
data.writeUtf8String(str);
String token = MD5.toBase64(data.toByteArray());
dis.close();
c.close();
return token;
}
} catch (Exception ex) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.systemPrintln("EX: " + ex.toString());
// #sijapp cond.end #
}
return null;
}
/**
* Service routine for google token
* (From mGTalk project)
*
* @param dis stream
* @return line from stream
*/
private String readLine(DataInputStream dis) {
StringBuilder s = new StringBuilder();
try {
for (byte ch = dis.readByte(); ch != -1; ch = dis.readByte()) {
if (ch == '\n') {
return s.toString();
}
s.append((char)ch);
}
} catch (Exception ignored) {
}
return s.toString();
}
private void updateConfPrivate(XmppServiceContact conf, String resource) {
String privateJid = Jid.realJidToJimmJid(conf.getUserId() + '/' + resource);
Contact privateContact = getJabber().getItemByUID(privateJid);
if (null != privateContact) {
((XmppServiceContact)privateContact).setPrivateContactStatus(conf);
getJabber().ui_changeContactStatus(privateContact);
}
}
public void updateContacts(Vector<Contact> contacts) {
StringBuilder xml = new StringBuilder();
int itemCount = 0;
xml.append("<iq type='set' id='").append(generateId())
.append("'><query xmlns='jabber:iq:roster'>");
for (int i = 0; i < contacts.size(); ++i) {
XmppContact contact = (XmppContact)contacts.elementAt(i);
if (Jid.isConference(contact.getUserId())) {
continue;
}
itemCount++;
xml.append("<item name='");
xml.append(Util.xmlEscape(contact.getName()));
xml.append("' jid='");
xml.append(Util.xmlEscape(contact.getUserId()));
Group group = getProtocol().getGroup(contact);
if (null != group) {
xml.append("'><group>");
xml.append(Util.xmlEscape(group.getName()));
xml.append("</group></item>");
} else {
xml.append("'/>");
}
}
xml.append("</query></iq>");
if (0 < itemCount) {
putPacketIntoQueue(xml.toString());
}
}
private void parseRosterExchange(XmlNode x, String domain) {
StringBuilder xml = new StringBuilder();
Xmpp j = (Xmpp)protocol;
Vector<XmppContact> subscribes = new Vector<XmppContact>();
for (int i = 0; i < x.childrenCount(); ++i) {
XmlNode item = x.childAt(i);
String jid = item.getAttribute(XmlNode.S_JID);
if (!jid.endsWith(domain)) {
continue;
}
boolean isDelete = item.getAttribute("a" + "ction").equals("d" + "elete");
boolean isModify = item.getAttribute("a" + "ction").equals("m" + "odify");
XmppContact contact = (XmppContact)j.getItemByUID(jid);
if (null == contact) {
if (isModify || isDelete) {
continue;
}
contact = (XmppContact)j.createTempContact(jid);
contact.setBooleanValue(Contact.CONTACT_NO_AUTH, true);
}
String group = item.getFirstNodeValue(S_GROUP);
if (!isDelete) {
contact.setName(item.getAttribute(XmlNode.S_NAME));
if (StringUtils.isEmpty(group)) {
group = contact.getDefaultGroupName();
}
contact.setGroup(j.getOrCreateGroup(group));
if ((null != group) && group.equals(contact.getDefaultGroupName())) {
group = null;
}
contact.setTempFlag(false);
if (!contact.isAuth()) {
subscribes.addElement(contact);
}
}
xml.append("<item jid='").append(Util.xmlEscape(jid));
if (isDelete) {
xml.append("' subscription='remove'/>");
continue;
} else if (!isModify) {
xml.append("' ask='subscribe");
}
xml.append("' name='");
xml.append(Util.xmlEscape(contact.getName()));
if (null != group) {
xml.append("'><group>")
.append(Util.xmlEscape(group))
.append("</group></item>");
} else {
xml.append("'/>");
}
}
if (0 < xml.length()) {
putPacketIntoQueue("<iq type='set' id='" + generateId()
+ "'><query xmlns='jabber:iq:roster'>"
+ xml.toString() + "</query></iq>");
xml = new StringBuilder();
for (int i = 0; i < subscribes.size(); ++i) {
xml.append("<presence type='subscribe' to='")
.append(Util.xmlEscape(((Contact)subscribes.elementAt(i)).getUserId()))
.append("'/>");
}
if (0 < xml.length()) {
putPacketIntoQueue(xml.toString());
}
}
}
public String getConferenceStorage() {
StringBuilder xml = new StringBuilder();
Vector contacts = getJabber().getContactItems();
xml.append("<storage xmlns='storage:bookmarks'>");
for (int i = 0; i < contacts.size(); ++i) {
XmppContact contact = (XmppContact)contacts.elementAt(i);
if (!contact.isConference() || contact.isTemp()) {
continue;
}
contact.setBooleanValue(Contact.CONTACT_NO_AUTH, false);
XmppServiceContact conf = (XmppServiceContact)contact;
xml.append("<conference autojoin='");
xml.append(conf.isAutoJoin() ? S_TRUE : S_FALSE);
xml.append("' name='");
xml.append(Util.xmlEscape(contact.getName()));
xml.append("' jid='");
xml.append(Util.xmlEscape(contact.getUserId()));
if (!StringUtils.isEmpty(conf.getPassword())) {
xml.append("' password='");
xml.append(Util.xmlEscape(conf.getPassword()));
}
xml.append("'><nick>");
xml.append(Util.xmlEscape(conf.getMyName()));
xml.append("</nick></conference>");
}
xml.append("</storage>");
return xml.toString();
}
public void saveConferences() {
StringBuilder xml = new StringBuilder();
String storage = getConferenceStorage();
xml.append("<iq type='set'><query xmlns='jabber:iq:private'>");
xml.append(storage);
xml.append("</query></iq>");
// XEP-0048
if (xep0048) {
xml.append("<iq type='set'>");
xml.append("<pubsub xmlns='http://jabber.org/protocol/pubsub'>");
xml.append("<publish node='storage:bookmarks'><item id='current'>");
xml.append(storage);
xml.append("</item></publish></pubsub></iq>");
}
putPacketIntoQueue(xml.toString());
}
public void removeGateContacts(String gate) {
if (StringUtils.isEmpty(gate)) {
return;
}
gate = "@" + gate;
Vector contacts = getJabber().getContactItems();
StringBuilder xml = new StringBuilder();
xml.append("<iq type='set' id='").append(generateId())
.append("'><query xmlns='jabber:iq:roster'>");
for (int i = 0; i < contacts.size(); ++i) {
XmppContact contact = (XmppContact)contacts.elementAt(i);
if (!contact.getUserId().endsWith(gate)) {
continue;
}
xml.append("<item subscription='remove' jid='");
xml.append(Util.xmlEscape(contact.getUserId()));
xml.append("'/>");
}
xml.append("</query></iq>");
putPacketIntoQueue(xml.toString());
}
public void updateContact(XmppContact contact) {
if (contact.isConference() && Jid.isConference(contact.getUserId()) && !isGTalk_) {
contact.setTempFlag(false);
contact.setBooleanValue(Contact.CONTACT_NO_AUTH, false);
String groupName = contact.getDefaultGroupName();
Group group = getJabber().getOrCreateGroup(groupName);
contact.setGroup(group);
saveConferences();
return;
}
Group g = getProtocol().getGroup(contact);
if (contact.isConference()) {
g = getJabber().getOrCreateGroup(contact.getDefaultGroupName());
} else if (g.getName().equals(contact.getDefaultGroupName())) {
g = null;
}
putPacketIntoQueue("<iq type='set' id='" + generateId()
+ "'><query xmlns='jabber:iq:roster'>"
+ "<item name='" + Util.xmlEscape(contact.getName())
+ "' jid='" + Util.xmlEscape(contact.getUserId()) + "'>"
+ (null == g ? "" : "<group>" + Util.xmlEscape(g.getName()) + "</group>")
+ "</item>"
+ "</query></iq>");
}
public void removeContact(String jid) {
if (Jid.isConference(jid) && !isGTalk_) {
saveConferences();
}
putPacketIntoQueue("<iq type='set' id='" + generateId()
+ "'><query xmlns='jabber:iq:roster'>"
+ "<item subscription='remove' jid='" + Util.xmlEscape(jid) + "'/>"
+ "</query></iq>");
}
public void getBookmarks() {
putPacketIntoQueue("<iq type='get' id='0'><query xmlns='jabber:iq:private'><storage xmlns='storage:bookmarks'/></query></iq>");
// XEP-0048
if (xep0048) {
putPacketIntoQueue("<iq type='get' id='1'><pubsub xmlns='http://jabber.org/protocol/pubsub'><items node='storage:bookmarks'/></pubsub></iq>");
}
}
/**
* Get open stream request
*/
private String getOpenStreamXml(String server) {
return "<?xml version='1.0'?>"
+ "<stream:stream xmlns='jabber:client' "
+ "xmlns:stream='http:/" + "/etherx.jabber.org/streams' "
+ "version='1.0' "
+ "to='" + server + "'"
+ " xml:lang='" + jimm.util.JLocale.getLanguageCode()+ "'>";
}
private void getVCard(String jid) {
putPacketIntoQueue("<iq type='get' to='" + Util.xmlEscape(jid) + "' id='"
+ Util.xmlEscape(generateId(S_VCARD)) + "'>"
+ "<vCard xmlns='vcard-temp' version='2.0' prodid='-/"
+ "/HandGen/" + "/NONSGML vGen v1.0/" + "/EN'/>"
+ "</iq>");
}
private void sendMessage(String to, String msg, String type, boolean notify, String id) {
to = Jid.jimmJidToRealJid(to);
boolean buzz = msg.startsWith(PlainMessage.CMD_WAKEUP) && S_CHAT.equals(type);
if (buzz) {
type = S_HEADLINE;
notify = false;
if (!getJabber().isContactOverGate(to)) {
msg = msg.substring(PlainMessage.CMD_WAKEUP.length()).trim();
if (StringUtils.isEmpty(msg)) {
msg = "/me " + JLocale.getString("wake_you_up");
}
}
}
if (Jid.isMrim(to)) {
msg = StringUtils.convert(StringUtils.JIMM2MRIM, msg);
}
String chatState = "";
if ((1 < Options.getInt(Options.OPTION_TYPING_MODE)) && S_CHAT.equals(type)) {
chatState = getChatStateTag(S_ACTIVE);
}
putPacketIntoQueue("<message to='" + Util.xmlEscape(to) + "'"
+ " type='" + type + "' id='" + Util.xmlEscape(id) + "'>"
+ (isGTalk_ ? "<nos:x value='disabled' xmlns:nos='google:nosave'/>" : "")
+ (buzz ? "<attention xmlns='urn:xmpp:attention:0'/>" : "")
+ "<body>" + Util.xmlEscape(msg) + "</body>"
+ (notify ? "<request xmlns='urn:xmpp:receipts'/><x xmlns='jabber:x:event'><offline/><delivered/></x>" : "")
+ chatState
+ "</message>");
}
void sendMessage(String to, String msg) {
String type = S_CHAT;
if (Jid.isConference(to) && (-1 == to.indexOf('/'))) {
type = S_GROUPCHAT;
}
sendMessage(to, msg, type, false, generateId());
}
/**
* Sends a message to a user
*
* @param message Message to send
*/
void sendMessage(PlainMessage message) {
String to = message.getRcvrUin();
XmppContact toContact = (XmppContact)protocol.getItemByUID(to);
if (null != toContact) {
to = toContact.getReciverJid();
}
String type = S_CHAT;
if (Jid.isConference(to) && (-1 == to.indexOf('/'))) {
type = S_GROUPCHAT;
}
message.setMessageId(Util.uniqueValue());
boolean notify = true;
sendMessage(to, message.getText(), type, S_CHAT.equals(type),
String.valueOf(message.getMessageId()));
if (notify) {
addMessage(message);
}
}
private String getChatStateTag(String state) {
return "<" + state + " xmlns='http://jabber.org/protocol/chatstates'/>";
}
void sendTypingNotify(String to, boolean composing) {
String tag = getChatStateTag(composing ? S_COMPOSING : S_PAUSED);
putPacketIntoQueue("<message to='" + Util.xmlEscape(to)
+ "' id='0'>" + tag + "</message>");
}
void sendPresence(XmppServiceContact conf) {
String to = conf.getUserId();
String xml = "";
if (conf.isConference()) {
to += "/" + conf.getMyName();
String xNode = "";
String password = conf.getPassword();
if (!StringUtils.isEmpty(password)) {
xNode += "<password>" + Util.xmlEscape(password) + "</password>";
}
long time = conf.hasChat() ? getJabber().getChatModel(conf).getLastMessageTime() : 0;
time = (0 == time) ? 24*60*60 : (Jimm.getCurrentGmtTime() - time);
xNode += "<history maxstanzas='20' seconds='" + time + "'/>";
if (!StringUtils.isEmpty(xNode)) {
xml += "<x xmlns='http://jabber.org/protocol/muc'>" + xNode + "</x>";
}
}
String status = getNativeStatus(getJabber().getProfile().statusIndex);
if (!StringUtils.isEmpty(status)) {
xml += "<show>" + status + "</show>";
}
xml = "<presence to='"+ Util.xmlEscape(to) + "'>" + xml
+ getCaps() + "</presence>";
putPacketIntoQueue(xml);
}
void sendPresenceUnavailable(String to) {
putPacketIntoQueue("<presence type='unavailable' to='" + Util.xmlEscape(to)
+ "'><status>I'll be back</status></presence>");
}
void setStatus(byte statusIndex, String msg, int priority) {
setStatus(getNativeStatus(statusIndex), msg, priority);
}
void setStatus(String status, String msg, int priority) {
// #sijapp cond.if modules_XSTATUSES is "true" #
// FIXME
String xXml = getQipXStatus();
if (0 != xXml.length()) {
msg = getJabber().getProfile().xstatusTitle;
String descr = getJabber().getProfile().xstatusDescription;
if (!StringUtils.isEmpty(descr)) {
msg = msg + " " + descr;
}
}
// #sijapp cond.end #
String xml = "<presence>"
+ (StringUtils.isEmpty(status) ? "" : "<show>" + status + "</show>")
+ (StringUtils.isEmpty(msg) ? "" : "<status>" + Util.xmlEscape(msg) + "</status>")
+ (0 < priority ? "<priority>" + priority + "</priority>" : "")
+ getCaps()
// #sijapp cond.if modules_XSTATUSES is "true" #
+ xXml
// #sijapp cond.end #
+ "</presence>";
putPacketIntoQueue(xml);
}
public void sendSubscribed(String jid) {
requestPresence(jid, "s" + "ubscribed");
}
public void sendUnsubscribed(String jid) {
requestPresence(jid, "u" + "nsubscribed");
}
public void requestSubscribe(String jid) {
requestPresence(jid, "s" + "ubscribe");
}
private void requestPresence(String jid, String type) {
putPacketIntoQueue("<presence type='" + Util.xmlEscape(type) + "' to='" + Util.xmlEscape(jid) + "'/>");
}
private void requestIq(String jid, String xmlns, String id) {
putPacketIntoQueue("<iq type='get' to='" + Util.xmlEscape(jid)
+ "' id='" + Util.xmlEscape(id) + "'><query xmlns='" + xmlns + "'/></iq>");
}
private void requestIq(String jid, String xmlns) {
requestIq(jid, xmlns, generateId());
}
public void requestClientVersion(String jid) {
requestIq(jid, "jabber:iq:version");
}
public void requestConferenceInfo(String jid) {
requestIq(jid, "http://jabber.org/protocol/disco#info");
}
public void requestConferenceUsers(String jid) {
requestIq(jid, "http://jabber.org/protocol/disco#items");
serviceDiscovery = getJabber().getServiceDiscovery();
}
public void requestDiscoItems(String server) {
requestIq(server, "http://jabber.org/protocol/disco#items");
serviceDiscovery = getJabber().getServiceDiscovery();
}
void requestRawXml(String xml) {
putPacketIntoQueue(xml);
}
public void setMucRole(String jid, String nick, String role) {
putPacketIntoQueue("<iq type='set' to='" + Util.xmlEscape(jid)
+ "'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='"
+ Util.xmlEscape(nick)
+ "' role='" + Util.xmlEscape(role)
+ "'/></query></iq>");
}
public void setMucAffiliation(String jid, String userJid, String affiliation) {
putPacketIntoQueue("<iq type='set' to='" + Util.xmlEscape(jid)
+ "'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='"
+ Util.xmlEscape(affiliation)
+ "' jid='" + Util.xmlEscape(userJid)
+ "'/></query></iq>");
}
UserInfo getUserInfo(Contact contact) {
singleUserInfo = new UserInfo(getJabber(), contact.getUserId());
getVCard(contact.getUserId());
return singleUserInfo;
}
void register2(XmppForm form, String rawXml, String jid) {
jabberForm = form;
autoSubscribeDomain = jid;
requestRawXml(rawXml);
}
private boolean isAutoGateContact(String jid) {
return !StringUtils.isEmpty(autoSubscribeDomain)
&& (jid.equals(autoSubscribeDomain) || jid.endsWith('@' + autoSubscribeDomain));
}
void register(String jid) {
jabberForm = new XmppForm(XmppForm.TYPE_REGISTER, getJabber(), jid);
requestIq(jid, "jabber:iq:register", jabberForm.getId());
jabberForm.show();
}
void unregister(String jid) {
putPacketIntoQueue("<iq type='set' to='" + Util.xmlEscape(jid)
+ "' id='unreg1'><query xmlns='jabber:iq:register'><remove/></query></iq>");
}
void requestOwnerForm(String jid) {
jabberForm = new XmppForm(XmppForm.TYPE_OWNER, getJabber(), jid);
requestIq(jid, "http://jabber.org/protocol/muc#owner", jabberForm.getId());
jabberForm.show();
}
// #sijapp cond.if modules_XSTATUSES is "true" #
private void sendXStatus(String xstatus, String text) {
String[] path = Util.explode(Util.xmlEscape(xstatus), ':');
StringBuilder sb = new StringBuilder();
String typeUrl = "http://jabber.org/protocol/" + path[0];
sb.append("<iq type='set' id='").append(generateId());
sb.append("'><pubsub xmlns='http://jabber.org/protocol/pubsub'>");
sb.append("<publish node='").append(typeUrl).append("'><item>");
sb.append("<").append(path[0]).append(" xmlns='").append(Util.xmlEscape(typeUrl));
if (1 == path.length) {
sb.append("'/>");
} else {
sb.append("'><").append(path[1]);
if (2 == path.length) {
sb.append("/>");
} else {
sb.append("><").append(path[2]).append("/></").append(path[1]).append(">");
}
if (!StringUtils.isEmpty(text)) {
sb.append("<text>").append(Util.xmlEscape(text)).append("</text>");
}
sb.append("</").append(path[0]).append(">");
}
sb.append("</item></publish></pubsub></iq>");
putPacketIntoQueue(sb.toString());
}
private String getQipXStatus() {
byte x = getJabber().getProfile().xstatusIndex;
if (XStatusInfo.XSTATUS_NONE == x) {
return "";
}
String code = Xmpp.xStatus.getCode(x);
if ((null == code) || !code.startsWith(XmppXStatus.XSTATUS_START)) {
return "";
}
if (code.equals(XmppXStatus.XSTATUS_TEXT_NONE)) {
return "";
}
String id = code.substring(XmppXStatus.XSTATUS_START.length());
return "<x xmlns='" + S_FEATURE_XSTATUS + "' id='"
+ Util.xmlEscape(id) + "'><title>"
+ Util.xmlEscape(getJabber().getProfile().xstatusTitle)
+ "</title></x>";
}
private static final String S_FEATURE_XSTATUS = "http://qip.ru/x-status";
void setXStatus() {
String xstatusCode = Xmpp.xStatus.getCode(getJabber().getProfile().xstatusIndex);
if (null == xstatusCode) {
return;
}
setXStatusToIcqTransports();
if (xstatusCode.startsWith(XmppXStatus.XSTATUS_START)) {
setStatus(getNativeStatus(getJabber().getProfile().statusIndex),
getJabber().getProfile().statusMessage, Xmpp.PRIORITY);
return;
}
final String mood = "mo"+"od";
final String activity = "acti" + "vity";
if (!xstatusCode.startsWith(mood)) {
sendXStatus(mood, null);
}
if (!xstatusCode.startsWith(activity)) {
sendXStatus(activity, null);
}
if (xstatusCode.startsWith(mood) || xstatusCode.startsWith(activity)) {
sendXStatus(xstatusCode, getJabber().getProfile().xstatusTitle);
}
}
private void setXStatusToIcqTransport(XmppServiceContact gate) {
String xstatus = Xmpp.xStatus.getIcqXStatus(getJabber().getProfile().xstatusIndex);
if (null == xstatus) {
return;
}
String desc = "None".equals(xstatus) ? null : getJabber().getProfile().xstatusTitle;
desc = StringUtils.notNull(desc);
if (gate.isOnline() && Jid.isPyIcqGate(gate.getUserId())) {
String out = "<iq type='set' id='" + generateId() + "' to='"
+ Util.xmlEscape(gate.getUserId())
+ "'><command xmlns='http://jabber.org/protocol/commands' node='setxstatus' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='xstatus_desc'><value>"
+ Util.xmlEscape(desc)
+ "</value></field><field var='xstatus_name'><value>"
+ Util.xmlEscape(xstatus)
+ "</value></field></x></command></iq>";
putPacketIntoQueue(out);
}
}
private void setXStatusToIcqTransports() {
String x = Xmpp.xStatus.getIcqXStatus(getJabber().getProfile().xstatusIndex);
if (null == x) {
return;
}
Vector contacts = getJabber().getContactItems();
for (int i = contacts.size() - 1; i >= 0; --i) {
XmppContact c = (XmppContact)contacts.elementAt(i);
if (c.isOnline() && Jid.isPyIcqGate(c.getUserId())) {
setXStatusToIcqTransport((XmppServiceContact)c);
}
}
}
// #sijapp cond.end #
private String getVerHash(Vector features) {
StringBuilder sb = new StringBuilder();
sb.append("client/phone/" + "/Jimm<");
for (int i = 0; i < features.size(); ++i) {
sb.append(features.elementAt(i)).append('<');
}
return MD5.toBase64(new MD5().calculate(StringUtils.stringToByteArrayUtf8(sb.toString())));
}
private String getFeatures(Vector features) {
StringBuilder sb = new StringBuilder();
sb.append("<identity category='client' type='phone' name='Jimm'/>");
for (int i = 0; i < features.size(); ++i) {
sb.append("<feature var='").append(features.elementAt(i)).append("'/>");
}
return sb.toString();
}
private void initFeatures() {
Vector<String> features = new Vector<String>();
features.addElement("bugs");
// #sijapp cond.if modules_XSTATUSES is "true" #
features.addElement("http://jabber.org/protocol/activity");
features.addElement("http://jabber.org/protocol/activity+notify");
// #sijapp cond.end #
// #sijapp cond.if modules_SOUND is "true" #
if (0 < Options.getInt(Options.OPTION_TYPING_MODE)) {
features.addElement("http://jabber.org/protocol/chatstates");
}
// #sijapp cond.end #
features.addElement("http://jabber.org/protocol/disco#info");
// #sijapp cond.if modules_XSTATUSES is "true" #
features.addElement("http://jabber.org/protocol/mood");
features.addElement("http://jabber.org/protocol/mood+notify");
// #sijapp cond.end #
features.addElement("http://jabber.org/protocol/rosterx");
// #sijapp cond.if modules_XSTATUSES is "true" #
features.addElement(S_FEATURE_XSTATUS);//"http://qip.ru/x-status");
// #sijapp cond.end #
features.addElement("jabber:iq:last");
features.addElement("jabber:iq:version");
features.addElement("urn:xmpp:attention:0");
features.addElement("urn:xmpp:time");
verHash = getVerHash(features);
featureList = getFeatures(features);
}
private boolean isMessageExist(String id) {
return isMessageExist(Util.strToIntDef(id, -1));
}
private void setMessageSent(String id, int state) {
markMessageSent(Util.strToIntDef(id, -1), state);
}
void resetAdHoc() {
adhoc = null;
}
void requestCommand(AdHoc adhoc, String node) {
this.adhoc = adhoc;
putPacketIntoQueue("<iq to='" + Util.xmlEscape(adhoc.getJid())
+ "' type='set' id='" + Util.xmlEscape(generateId()) + "'>"
+ "<command xmlns='http://jabber.org/protocol/commands' "
+ "node='" + Util.xmlEscape(node) + "'/></iq>");
}
void requestCommandList(AdHoc adhoc) {
this.adhoc = adhoc;
putPacketIntoQueue("<iq type='get' to='" + Util.xmlEscape(adhoc.getJid())
+ "' id='" + Util.xmlEscape(generateId()) + "'><query xmlns='"
+ "http://jabber.org/protocol/disco#items"
+ "' node='http://jabber.org/protocol/commands'/></iq>");
}
// #sijapp cond.if modules_FILES is "true"#
void setIBB(IBBFileTransfer transfer) {
ibb = transfer;
ibb.setProgress(0);
putPacketIntoQueue(ibb.getRequest());
}
private boolean processIbb(XmlNode iq, byte type, String id) {
id = StringUtils.notNull(id);
if (!id.startsWith("jimmibb_")) {
return false;
}
if (IQ_TYPE_RESULT != type) {
// something bad happend
ibb.setProgress(-1);
ibb.destroy();
ibb = null;
return true;
}
if ("jimmibb_si".equals(id)) {
ibb.setProgress(10);
putPacketIntoQueue(ibb.initTransfer());
return true;
}
if ("jimmibb_close".equals(id)) {
return true;
}
if (ibb.isCanceled()) {
putPacketIntoQueue(ibb.close());
ibb.destroy();
ibb = null;
return true;
}
ibb.setProgress(ibb.getPercent());
String stanza = ibb.nextBlock();
if (null == stanza) {
stanza = ibb.close();
ibb.setProgress(100);
ibb.destroy();
ibb = null;
}
putPacketIntoQueue(stanza);
return true;
}
// #sijapp cond.end#
}
// #sijapp cond.end #