/* Copyright (c) 2008 Bluendo S.r.L.
* See about.html for details about license.
*
* $Id: XMPPClient.java 1597 2009-06-19 11:54:12Z luca $
*/
package it.yup.xmpp;
import it.yup.transport.BaseChannel;
import it.yup.transport.SocketChannel;
// #mdebug
//@
//@import it.yup.util.Logger;
//@
//#enddebug
import it.yup.util.RMSIndex;
import it.yup.util.Utils;
import it.yup.xml.Element;
import it.yup.xmlstream.AccountRegistration;
import it.yup.xmlstream.BasicXmlStream;
import it.yup.xmlstream.EventQuery;
import it.yup.xmlstream.EventQueryRegistration;
import it.yup.xmlstream.PacketListener;
import it.yup.xmlstream.SocketStream;
import it.yup.xmlstream.StreamEventListener;
import it.yup.xmpp.packets.DataForm;
import it.yup.xmpp.packets.Iq;
import it.yup.xmpp.packets.Message;
import it.yup.xmpp.packets.Presence;
import it.yup.xmpp.packets.Stanza;
import java.io.IOException;
import java.util.Enumeration;
import java.util.TimerTask;
import java.util.Vector;
import org.bouncycastle.util.encoders.Base64;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Image;
public class XMPPClient implements StreamEventListener {
/*
* Few methods used to communicate to the application of xmpp events
*/
public interface XmppListener {
void removeContact(Contact c);
void removeAllContacts();
void updateContact(Contact c, int chStatus);
void authenticated();
void rosterXsubscription(Element e);
void playSmartTone();
void askSubscription(Contact u);
void handleCommand(Contact c, String preferredResource);
void connectionLost();
void showAlert(AlertType type, String title, String text,
Object next_screen);
void handleTask(Task task, boolean display);
Object handleDataForm(DataForm df, byte type, DataFormListener dfl,
int cmds);
void commandExecuted(Object screenToClose);
void showCommand(Object screen);
void rosterRetrieved();
}
private Config cfg = Config.getInstance();
/*
* The features published by Lampiro are ordered as specified here:
* http://tools.ietf.org/html/rfc4790#section-9.3
*/
private String[] features = new String[] { MIDP_PLATFORM, NS_CAPS,
NS_COMMANDS, NS_IQ_DISCO_INFO, NS_IBB, NS_MUC, NS_ROSTERX,
JABBER_X_DATA, JINGLE, JINGLE_FILE_TRANSFER, JINGLE_IBB_TRANSPORT };
/** the client instance */
private static XMPPClient xmppInstance;
// /** the authID value obtained during stream initialization */
// public String _authID;
public Roster roster;
/** myself */
private Contact me;
/** my jabber id */
public String my_jid;
/** the used XmlStream */
private BasicXmlStream xmlStream = null;
/** The actual connection with the Server */
private BaseChannel connection = null;
/** true when the stream is valid */
private boolean valid_stream = false;
private EventQueryRegistration lostConnReg = null;
// /** send the subscribe at most once per session */
// private boolean lampiro_subscribe_sent = false;
protected Image presence_icons[];
protected Image presence_phone_icons[];
/*
* This task is used to retrieve asynchronously the roster
* after going online.
*/
private TimerTask rosterRetrieveTask = null;
/*
* The time after rosterRetrieveTask is scheduled after receiving
* a presence
*/
private int rosterRetrieveTime = 5000;
/*
* The time at which the last presence has been received
*/
private long lastPresenceTime;
/*
* The number of sent bytes over the socket
*/
//public static int bytes_sent = 0;
/*
* The number of received bytes over the socket
*/
//public static int bytes_received = 0;
/*
* A flag used to enable or disable compression
*/
public boolean addCompression = false;
/*
* A flag used to enable or disable TLS
*/
public boolean addTLS = false;
/*
* the gateways whose contacts must be autoaccepted
* i.e. the gateways whose presence has been subscripted
* within the current session
*/
public Vector autoAcceptGateways = new Vector();
private XMPPClient.XmppListener xmppListener;
/*
* Used to notify the XmppClient that the user jid and/or password
* are changed from last login
*/
private boolean newCredentials = false;
/*
* The registration initializer
*/
private AccountRegistration accountRegistration;
/**
* Get the total amount of traffic on the GPRS connection
*
* @return an array with two elements: in / out traffic
*/
public static int[] getTraffic() {
return new int[] { BaseChannel.bytes_received, BaseChannel.bytes_sent };
}
public static String NS_IQ_DISCO_INFO = "http://jabber.org/protocol/disco#info";
public static String NS_IQ_DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
public static String NS_COMMANDS = "http://jabber.org/protocol/commands";
public static String NS_CAPS = "http://jabber.org/protocol/caps";
public static String NS_BLUENDO_CAPS = "http://bluendo.com/protocol/caps";
public static String NS_BLUENDO_PUBLISH = "bluendo:http:publish:0";
public static String NS_IBB = "http://jabber.org/protocol/ibb";
public static String NS_MUC = "http://jabber.org/protocol/muc";
public static String NS_ROSTERX = "http://jabber.org/protocol/rosterx";
public static String NS_MUC_USER = "http://jabber.org/protocol/muc#user";
public static String NS_MUC_OWNER = "http://jabber.org/protocol/muc#owner";
public static String NS_VCARD_UPDATE = "vcard-temp:x:update";
public static String NS_NICK = "http://jabber.org/protocol/nick";
public static String MIDP_PLATFORM = "http://bluendo.com/midp#platform";
public static String JABBER_X_DATA = "jabber:x:data";
public static String JABBER_IQ_GATEWAY = "jabber:iq:gateway";
public static String IQ_REGISTER = "jabber:iq:register";
public static String JINGLE = "urn:xmpp:jingle:0";
public static String JINGLE_FILE_TRANSFER = "urn:xmpp:jingle:apps:file-transfer:1";
public static String FILE_TRANSFER = "http://jabber.org/protocol/si/profile/file-transfer";
public static String JINGLE_IBB_TRANSPORT = "urn:xmpp:jingle:transports:ibb:0";
public static String BLUENDO_PUBLISH = "bluendo:http:publish:0";
public static String USERID = "USERID";
public static String VCARD_TEMP = "vcard-temp";
public static String VCARD = "vCard";
public static String BINVAL = "BINVAL";
public static String NICKNAME = "NICKNAME";
public static String PHOTO = "PHOTO";
public static String FN = "FN";
public static String EMAIL = "EMAIL";
public static String BLUENDO_XMLRPC = "bluendo:bxmlrpc:0";
public static String BLUENDO_REGISTER = "bluendo:register:0";
public static String NS_DELAY = "urn:xmpp:delay";
public static String DELAY = "delay";
public static String[][] errorCodes = new String[][] {
{ "400", "Bad request" }, { "401", "Not authorized" },
{ "403", "Forbidden" }, { "404", "Item not found" },
{ "406", "Not acceptable" },
{ "407", "Registration required" },
{ "500", "Internal server error" },
{ "501", "Feature not implemented" },
{ "503", "Service unavailable" }, };
public Image getPresenceIcon(Contact c, String preferredResource,
int availability) {
Presence p = null;
if (c != null) {
if (preferredResource != null) p = c.getPresence(preferredResource);
else
p = c.getPresence();
}
if (availability >= 0 && availability < this.presence_icons.length) {
if (p == null || p.pType == Presence.PC) {
return this.presence_icons[availability];
} else if (p.pType == Presence.PHONE) { return this.presence_phone_icons[availability]; }
}
return null; // maybe we could return an empty image
}
private XMPPClient() {
roster = new Roster(this);
// preload the presence icons
String mapping[] = Contact.availability_mapping;
presence_icons = new Image[mapping.length];
presence_phone_icons = new Image[mapping.length];
try {
presence_icons[0] = Image.createImage("/icons/presence_"
+ mapping[1] + ".png");
presence_phone_icons[0] = Image.createImage("/icons/presence_"
+ mapping[1] + "_phone.png");
presence_icons[1] = Image.createImage("/icons/presence_"
+ mapping[1] + ".png");
presence_phone_icons[1] = Image.createImage("/icons/presence_"
+ mapping[1] + "_phone.png");
presence_icons[2] = Image.createImage("/icons/presence_"
+ mapping[2] + ".png");
presence_phone_icons[2] = Image.createImage("/icons/presence_"
+ mapping[2] + "_phone.png");
presence_icons[3] = Image.createImage("/icons/presence_"
+ mapping[3] + ".png");
presence_phone_icons[3] = Image.createImage("/icons/presence_"
+ mapping[3] + "_phone.png");
presence_icons[4] = Image.createImage("/icons/presence_"
+ mapping[3] + ".png");
presence_phone_icons[4] = Image.createImage("/icons/presence_"
+ mapping[3] + "_phone.png");
presence_icons[5] = Image.createImage("/icons/presence_"
+ mapping[5] + ".png");
presence_phone_icons[5] = Image.createImage("/icons/presence_"
+ mapping[5] + "_phone.png");
} catch (Exception e) {
// should not happen
}
// presence_icons = new Image[mapping.length];
// for (int i = 0; i < presence_icons.length; i++) {
// try {
// presence_icons[i] = Image.createImage("/icons/presence_"
// + mapping[i] + ".png");
// } catch (IOException e) {
// presence_icons[i] = Image.createImage(16, 16);
// }
// }
}
/**
* Get the XMPP client (a singleton)
*
* @return the unique instance of the client
*
*/
public static XMPPClient getInstance() {
if (xmppInstance == null) {
xmppInstance = new XMPPClient();
}
return xmppInstance;
}
public void startClient() {
}
/** close the connection XXX i don't like this name */
public void stopClient() {
// saveToStorage();
}
// /**
// * Add a listener for XMPP packets
// *
// * @param _q
// * the xpath like query
// * @param _l
// * the listener
// * @return The registration object to be used with
// * {@link #unregisterListener(EventQueryRegistration)} for removing
// * the listener
// */
// public EventQueryRegistration registerListener(EventQuery _q, Object _l) {
// return BasicXmlStream.addEventListener(_q, _l);
// }
//
// /**
// * Add a one time listener for XMPP packets
// *
// * @param _q
// * the xpath like query
// * @param _l
// * the listener
// * @return The registration object to be used with
// * {@link #unregisterListener(EventQueryRegistration)} for removing
// * the listener
// *
// */
// public EventQueryRegistration registerOneTimeListener(EventQuery _q,
// Object _l) {
// return BasicXmlStream.addOnetimeEventListener(_q, _l);
// }
//
// /**
// * Remove a registered listener
// *
// * @param reg
// * the registration obtained with
// * {@link #registerListener(EventQuery, PacketListener)}
// *
// */
// public void unregisterListener(EventQueryRegistration reg) {
// BasicXmlStream.removeEventListener(reg);
// }
/**
* Queue a packet into the send queue
*
* @param pack
* the packet to be sent
*/
public void sendPacket(Element pack) {
xmlStream.send(pack, Config.TIMEOUT);
}
/**
* Send an Iq packet and register the packet listener for the answer
*
* @param iq
* @param listener
* (may be null)
*/
public void sendIQ(Iq iq, IQResultListener listener) {
if (listener != null) {
IqManager.getInstance().addRegistration(iq, listener);
}
sendPacket(iq);
}
public Contact getMyContact() {
return me;
}
/**
* Start the XML Stream using the current configuration
*
* @param register
* set true if the stream must create the account
* @param newCredentials
* @return
*/
public BasicXmlStream createStream(boolean register, boolean newCredentials) {
this.newCredentials = newCredentials;
// this must be done to clean the Roster after a reconnect
this.roster.purge();
buildSocketConnection();
// connection = new SimpleBTConnector(
// Config.HTTP_GW_HOST,
// Config.HTTP_GW_PATH,
// xmlStream
// );
// XXX useful with messages? I don't think so
// eq = new EventQuery(Message.MESSAGE, null, null);
// eq.child = new EventQuery(Iq.QUERY, new String[] { "xmlns" },
// new String[] { "http://jabber.org/protocol/disco#items" } );
// xmlStream.addEventListener(eq, adch);
if (register) {
EventQuery qReg = new EventQuery(
BasicXmlStream.STREAM_ACCOUNT_REGISTERED, null, null);
/*
* The registration used to be notified of the registration
*/
BasicXmlStream.addEventListener(qReg, this);
accountRegistration = new AccountRegistration();
xmlStream.addInitializer(accountRegistration, 0);
}
return xmlStream;
}
/**
* Build the low level connection based on plain sockets
*/
private void buildSocketConnection() {
xmlStream = new SocketStream();
// #ifndef BT_PLAIN_SOCKET
connection = new SocketChannel("socket://"
+ cfg.getProperty(Config.CONNECTING_SERVER), xmlStream);
// #endif
((SocketChannel) connection).KEEP_ALIVE = Long.parseLong(cfg
.getProperty(Config.KEEP_ALIVE));
}
public void openStream() {
String resource = cfg.getProperty(Config.YUP_RESOURCE, "Lampiro");
xmlStream.initialize(cfg.getProperty(Config.USER) + "@"
+ cfg.getProperty(Config.SERVER) + "/" + resource, cfg
.getProperty(Config.PASSWORD));
EventQuery qAuth = new EventQuery(BasicXmlStream.STREAM_INITIALIZED,
null, null);
/*
* The registration used to be notified of the authentication
*/
BasicXmlStream.addEventListener(qAuth, this);
if (!connection.isOpen()) {
connection.open();
}
}
public void closeStream() {
if (connection.isOpen()) {
connection.close();
}
}
public void gotStreamEvent(String event, Object source) {
if (BasicXmlStream.STREAM_INITIALIZED.equals(event)) {
// all these registration are made here
// Register the handler for incoming messages
EventQuery eq = new EventQuery(Message.MESSAGE, null, null);
eq.child = new EventQuery(Message.BODY, null, null);
BasicXmlStream.addEventListener(eq, new MessageHandler());
// Register the presence handler
eq = new EventQuery(Presence.PRESENCE, null, null);
BasicXmlStream.addEventListener(eq, new PresenceHandler());
// Register the disco handler
eq = new EventQuery(Iq.IQ, new String[] { "type" },
new String[] { "get" });
eq.child = new EventQuery(Iq.QUERY, new String[] { "xmlns" },
new String[] { NS_IQ_DISCO_INFO });
BasicXmlStream.addEventListener(eq, new DiscoHandler());
// Register the handler for dataforms (both as <iq/> and <message/>)
// payloads
DataFormHandler dh = new DataFormHandler();
eq = new EventQuery(Message.MESSAGE, null, null);
eq.child = new EventQuery(DataForm.X, new String[] { "xmlns" },
new String[] { DataForm.NAMESPACE });
BasicXmlStream.addEventListener(eq, dh);
eq = new EventQuery(Iq.IQ, null, null);
eq.child = new EventQuery(DataForm.X, new String[] { "xmlns" },
new String[] { DataForm.NAMESPACE });
BasicXmlStream.addEventListener(eq, dh);
/* register handler for ad hoc command announcements */
PacketListener ashc_listener = new PacketListener() {
public void packetReceived(Element e) {
handleClientCommands(e, false);
}
};
// XXX here we *must* use client capabilities
/* register handler for ad hoc command presence announce */
eq = new EventQuery(Presence.PRESENCE, null, null);
eq.child = new EventQuery(Iq.QUERY, new String[] { "xmlns" },
new String[] { "http://jabber.org/protocol/disco#items" });
BasicXmlStream.addEventListener(eq, ashc_listener);
IqManager.getInstance().streamInitialized();
roster.streamInitialized();
stream_authenticated();
} else if (BasicXmlStream.STREAM_ACCOUNT_REGISTERED.equals(event)) {
xmlStream.removeInitializer(accountRegistration);
}
}
public void stream_authenticated() {
// create the self contact and setup the initial presence
my_jid = xmlStream.jid;
me = new Contact(Contact.userhost(my_jid), null, null, null);
Presence p = new Presence();
p.setAttribute("from", my_jid);
String show = cfg.getProperty(Config.LAST_PRESENCE_SHOW);
if (show != null && !"online".equals(show)) {
p.setShow(show);
}
String msg = cfg.getProperty(Config.LAST_STATUS_MESSAGE);
String tempPriority = cfg.getProperty(Config.LAST_PRIORITY, "0");
p.setPriority(Integer.parseInt(tempPriority));
p.setStatus(msg);
// set capabilities
String uri = NS_CAPS;
Element cap = p.addElement(uri, "c");
cap.setAttribute("node", XMPPClient.NS_BLUENDO_CAPS);
cap.setAttribute("hash", "sha-1");
cap.setAttribute("ver", getCapVer());
// XXX I don't like this, it could be better to send capabilities with a
// different hash in the version
// Element x = p.addElement(JABBER_X_DATA, "x");
// x.setAttribute("type", Iq.T_RESULT);
// Element field = x.addElement(JABBER_X_DATA, "field");
// field.setAttribute("var", "FORM_TYPE");
// field.setAttribute("type", "hidden");
// field.addElement(JABBER_X_DATA, "value").addText(MIDP_PLATFORM);
//
// field = x.addElement(JABBER_X_DATA, "field");
// field.setAttribute("var", "microedition.platform");
// field.addElement(JABBER_X_DATA, "value").addText(
// System.getProperty("microedition.platform"));
me.updatePresence(p);
// we are connected, set the stream as valid
valid_stream = true;
// Listen for lost connections
lostConnReg = BasicXmlStream.addEventListener(new EventQuery(
BasicXmlStream.STREAM_TERMINATED, null, null),
new StreamEventListener() {
public void gotStreamEvent(String event, Object source) {
if (rosterRetrieveTask != null) rosterRetrieveTask
.cancel();
valid_stream = false;
closeStream();
if (xmppListener != null) xmppListener.connectionLost();
showAlert(AlertType.ERROR, "Connection lost",
"Connection with the server lost", null);
BasicXmlStream.removeEventListener(lostConnReg);
/* XXX: should close all screens and open the RegisterScreen */
}
});
// if this is the first login it is better to ask the roster and go online suddenly
// otherwise i only go online and the ask the roster after a while
// however first try to load the roster from db
String rmsName = "rstr_"
+ Contact.userhost(my_jid).replace('@', '_').replace('.', '_');
if (rmsName.length() > 31) {
rmsName = rmsName.substring(0, 31);
}
roster.rosterStore = new RMSIndex(rmsName);
roster.readFromStorage();
if (newCredentials || Integer.parseInt(this.roster.rosterVersion) > 0) {
roster.retrieveRoster(true, false);
} else {
this.setPresence(-1, null);
lastPresenceTime = System.currentTimeMillis();
rosterRetrieveTask = new TimerTask() {
public void run() {
if (lastPresenceTime + rosterRetrieveTime < System
.currentTimeMillis()) {
roster.retrieveRoster(false, false);
this.cancel();
}
}
};
Utils.tasks.schedule(rosterRetrieveTask, rosterRetrieveTime,
rosterRetrieveTime);
}
if (this.xmppListener != null) xmppListener.authenticated();
}
private String getCapVer() {
Vector ss = new Vector();
ss.addElement("client/");
ss.addElement("phone/");
ss.addElement("/")/* XXX should be the lang here */;
ss.addElement("Lampiro " + cfg.getProperty(Config.VERSION) + "<");
for (int i = 0; i < features.length; i++) {
ss.addElement(features[i]);
ss.addElement("<");
}
Enumeration en = ss.elements();
String S = "";
while (en.hasMoreElements()) {
S += en.nextElement();
}
S = new String(Base64.encode(Utils.digest(S, "sha1")));
return S;
}
private class MessageHandler implements PacketListener {
public void packetReceived(Element p) {
// different behaviours depending on type
String type = p.getAttribute(Message.ATT_TYPE);
if (type == null) type = Message.NORMAL;
// e.g. normal are used to receive invite for MUC;
// if the MUC user is created here it result in a normal Contact!!!
// be careful for that
Element x = p.getChildByName(XMPPClient.NS_MUC_USER, "x");
if (x!= null){
Element invite = x.getChildByName(null, "invite");
if (invite !=null)
return;
}
Message msg = new Message(p);
// XXX: we will need to check the type
String jid = msg.getAttribute(Stanza.ATT_FROM);
// error packet sometimes do not have from
if (jid == null) return;
Contact u = roster.getContactByJid(jid);
if (u == null) {
Element group_elements[] = p.getChildrenByName(null, "group");
String groups[] = new String[group_elements.length];
for (int j = 0; j < groups.length; j++) {
groups[j] = group_elements[j].getText();
}
u = new Contact(Contact.userhost(jid), p.getAttribute("name"),
p.getAttribute("subscription"), groups);
roster.contacts.put(Contact.userhost(u.jid), u);
}
u.addMessageToHistory(jid, msg);
if (xmppListener != null) xmppListener.updateContact(u,
Contact.CH_MESSAGE_NEW);
playSmartTone();
}
}
private class PresenceHandler implements PacketListener {
public void packetReceived(Element e) {
// #mdebug
//@ Logger.log("PresenceHandler: received packet: "
//@ + new String(e.toXml()), Logger.DEBUG);
// #enddebug
lastPresenceTime = System.currentTimeMillis();
String t = e.getAttribute(Stanza.ATT_TYPE);
if (t == null || Presence.T_UNAVAILABLE.equals(t)) {
Presence p = new Presence(e);
String from = e.getAttribute(Stanza.ATT_FROM);
Contact u = roster.getContactByJid(from);
String type = e.getAttribute(Stanza.ATT_TYPE);
if (u == null) {
// first check if its a MUC
Element[] xs = p.getChildrenByName(null, "x");
for (int i = 0; xs != null && i < xs.length; i++) {
if (xs[i].uri != null
&& xs[i].uri.indexOf(XMPPClient.NS_MUC) >= 0) {
u = new MUC(Contact.userhost(from), Contact
.user(from));
}
}
if (u == null) {
if (type != null
&& type.compareTo(Presence.T_UNAVAILABLE) == 0) return;
// XXX Guess the subscription
u = new Contact(Contact.userhost(from), null,
"unknown", null);
}
u.updatePresence(new Presence(e));
roster.contacts.put(u.jid, u);
} else {
u.updatePresence(p);
}
if (xmppListener != null) xmppListener.updateContact(u,
Contact.CH_STATUS);
} else if (Presence.T_SUBSCRIBE.equals(t)) {
handleSubscribe(new Presence(e));
} else {
// XXX At present ignore other cases, but when receiving
// UNSUBCRIBED we should update the roster
}
}
private void handleSubscribe(Presence p) {
// try getting the contact (we may already have it)
String jid = Contact.userhost(p.getAttribute(Stanza.ATT_FROM));
Contact u = roster.getContactByJid(jid);
if (u == null) {
// we don't have the contact, create it
u = new Contact(jid, null, null, null);
}
// subscription handling
if ("both".equals(u.subscription) || "to".equals(u.subscription)
|| Config.LAMPIRO_AGENT.equals(Contact.userhost(jid))) {
// subscribe received: if already granted, I don't ask anything
Presence pmsg = new Presence();
pmsg.setAttribute(Stanza.ATT_TO, u.jid);
pmsg.setAttribute(Stanza.ATT_TYPE, Presence.T_SUBSCRIBED);
sendPacket(pmsg);
} else {
/*
* UIMenu confirmMenu = new UIMenu(rm
* .getString(ResourceIDs.STR_SUBSCRIPTION_CONFIRM)); UILabel
* confirmQuestion = new UILabel(rm
* .getString(ResourceIDs.STR_SUBSCRIPTION_REQUEST_FROM) + " " +
* u.jid + ". " +
* rm.getString(ResourceIDs.STR_SUBSCRIPTION_ACCEPT));
* confirmMenu.append(confirmQuestion);
* confirmQuestion.setFocusable(true);
* confirmMenu.setSelectedIndex(1);
* confirmMenu.setAbsoluteX(10);
* confirmMenu.setWidth(UICanvas.getInstance().getWidth() - 20);
* confirmQuestion.setWrappable(true, confirmQuestion.getWidth() -
* 5); confirmMenu.cancelMenuString =
* rm.getString(ResourceIDs.STR_NO);
* confirmMenu.selectMenuString = rm
* .getString(ResourceIDs.STR_YES); UIScreen currentScreen =
* UICanvas.getInstance() .getCurrentScreen(); Graphics cg =
* currentScreen.getGraphics(); int offset = (cg.getClipHeight() -
* confirmMenu.getHeight(cg)) / 2;
* confirmMenu.setAbsoluteY(offset); this.confirmContact =
* contact; currentScreen.addPopup(confirmMenu);
*/
// add a nick only if a previous name has been added
Element nick = p.getChildByName(XMPPClient.NS_NICK, "nick");
if (nick != null) {
String nickNameText = nick.getText();
if (nickNameText != null && nickNameText.length() > 0
&& (u.name == null || u.name.length() == 0)) {
u.name = nickNameText;
}
}
Enumeration en = XMPPClient.this.autoAcceptGateways.elements();
while (en.hasMoreElements()) {
String ithGateway = (String) en.nextElement();
if (u.jid.indexOf(ithGateway) >= 0) {
XMPPClient.this.roster.subscribeContact(u, true);
return;
}
}
if (xmppListener != null) {
xmppListener.askSubscription(u);
}
}
}
}
// XXX The roster handler should always listen to any iq:roster packet for
// supporting roster push and any roster update when logged in
private class DataFormHandler implements PacketListener {
public void packetReceived(Element p) {
updateTask(new SimpleDataFormExecutor(p));
playSmartTone();
}
}
private class DiscoHandler implements PacketListener {
public void packetReceived(Element p) {
Element q = p.getChildByName(NS_IQ_DISCO_INFO, Iq.QUERY);
Iq reply = new Iq(p.getAttribute(Stanza.ATT_FROM), Iq.T_RESULT);
reply.setAttribute(Stanza.ATT_ID, p.getAttribute(Stanza.ATT_ID));
String node = q.getAttribute("node");
Element qr = reply.addElement(NS_IQ_DISCO_INFO, Iq.QUERY);
String capDisco = XMPPClient.NS_BLUENDO_CAPS + "#"
+ XMPPClient.this.getCapVer();
if (node == null || node.compareTo(capDisco) == 0) {
Element identity = qr.addElement(NS_IQ_DISCO_INFO, "identity");
identity.setAttribute("category", "client");
identity.setAttribute("type", "phone");
identity.setAttribute("name", "Lampiro");
for (int i = 0; i < features.length; i++) {
Element feature = qr
.addElement(NS_IQ_DISCO_INFO, "feature");
feature.setAttribute("var", features[i]);
}
} else if (MIDP_PLATFORM.equals(node)) {
qr.setAttribute("node", MIDP_PLATFORM);
Element x = qr.addElement(JABBER_X_DATA, "x");
x.setAttribute("type", Iq.T_RESULT);
Element field = x.addElement(JABBER_X_DATA, "field");
field.setAttribute("var", "microedition.platform");
field.addElement(JABBER_X_DATA, "value").addText(
System.getProperty("microedition.platform"));
}
if (node != null && node.compareTo(capDisco) == 0) {
qr.setAttribute("node", capDisco);
}
sendPacket(reply);
}
}
public void playSmartTone() {
if (this.xmppListener != null) {
xmppListener.playSmartTone();
}
}
/*
* Set the current priority of the client (store and send it).
* After setting the priority calls setpresence.
*
* @param priority The priority to set
*/
public void setPresence(int availability, String status, int priority) {
Presence p = me.getPresence(my_jid);
p.setPriority(priority);
this.setPresence(availability, status);
}
/**
* Set the current presence of the client (store and send it)
*
* @param availability
* @param status
*/
public void setPresence(int availability, String status) {
Presence p = me.getPresence(my_jid);
if (p == null) return;
Presence new_p = new Presence();
new_p.setAttribute("from", p.getAttribute("from"));
if (availability >= 0) {
if (Contact.AV_ONLINE == availability) {
} else if (Contact.AV_UNAVAILABLE == availability) {
new_p.setAttribute(Stanza.ATT_TYPE, Presence.T_UNAVAILABLE);
} else {
new_p.setShow(Contact.availability_mapping[availability]);
}
}
if (status != null) {
new_p.setStatus(status);
} else {
new_p.setStatus(p.getStatus());
}
//new_p.addElement(p.getChildByName(null, "x"));
new_p.addElement(p.getChildByName(null, "c"));
new_p.setPriority(p.getPriority());
me.updatePresence(new_p);
sendPacket(new_p);
if (Presence.T_UNAVAILABLE.equals(new_p.getAttribute(Stanza.ATT_TYPE))) {
closeStream();
}
}
/**
* Handle an incoming command list
*
* @param e
* the received element with commands
* @param show
* when true show the command list screen
*/
public void handleClientCommands(Element e, boolean show) {
String from = e.getAttribute(Stanza.ATT_FROM);
if (from == null) return;
Contact c = roster.getContactByJid(from);
if (c == null) return;
Element q = e.getChildByName("http://jabber.org/protocol/disco#items",
Iq.QUERY);
if (q != null) {
Element items[] = q.getChildrenByName(
"http://jabber.org/protocol/disco#items", "item");
c.cmdlist = new String[items.length][2];
for (int i = 0; i < items.length; i++) {
c.cmdlist[i][0] = items[i].getAttribute("node");
c.cmdlist[i][1] = items[i].getAttribute("name");
}
if (xmppListener != null) xmppListener.updateContact(c,
Contact.CH_TASK_NEW);
if (show && this.xmppListener != null) {
this.xmppListener.handleCommand(c, from);
}
} // XXX we could add an alert if it's empty and we have to show
}
/**
* Show an error screen, if multiple errors occur only append the message
*
* @param type
* @param title
* Title of the screen
* @param text
* Displayed error message
* @param next_screen
* the screen where we have to return to
*/
public void showAlert(AlertType type, String title, String text,
final Object next_screen) {
if (xmppListener != null) {
xmppListener.showAlert(type, title, text, next_screen);
}
}
/**
* Update the status of a task. Queue it if this is the first time it's
* status is updated
*
* @param task
*/
public void updateTask(Task task) {
Contact user = roster.getContactByJid(task.getFrom());
user.addTask(task);
// #mdebug
//@ System.out.println("Tsk: " + Integer.toHexString(task.getStatus()));
//#enddebug
byte type = task.getStatus();
// true if we should display the command
boolean display = false;
boolean removed = false;
if ((type & Task.CMD_MASK) == Task.CMD_MASK) {
switch (type) {
case Task.CMD_FORM_LESS:
display = false;
removed = true;
user.removeTask(task);
break;
case Task.CMD_INPUT:
display = true;
break;
case Task.CMD_EXECUTING:
// do nothing, just wait for an answer
break;
case Task.CMD_CANCELING:
// do nothing, just wait for an answer
break;
case Task.CMD_CANCELED:
display = true;
removed = true;
user.removeTask(task);
break;
case Task.CMD_FINISHED:
// tasks.removeElement(task);
display = true;
break;
case Task.CMD_ERROR:
display = true;
removed = true;
break;
case Task.CMD_DESTROY:
removed = true;
user.removeTask(task);
break;
}
} else { // simple data form
switch (type) {
case Task.DF_FORM:
display = true;
break;
case Task.DF_SUBMITTED:
removed = true;
user.removeTask(task);
break;
case Task.DF_CANCELED:
removed = true;
user.removeTask(task);
break;
case Task.DF_RESULT:
display = true;
break;
case Task.DF_ERROR:
display = true;
removed = true;
break;
case Task.DF_DESTROY:
removed = true;
user.removeTask(task);
break;
}
}
// update contact position in the roster
if (xmppListener != null) {
if (removed) {
xmppListener.updateContact(user, Contact.CH_TASK_REMOVED);
} else {
xmppListener.updateContact(user, Contact.CH_TASK_NEW);
}
}
if (this.xmppListener != null) {
this.xmppListener.handleTask(task, display);
}
};
// private void subscribe_to_agent() {
// if(lampiro_subscribe_sent){
// return;
// }
// lampiro_subscribe_sent = true;
//
// Contact c = getContactByJid(Config.LAMPIRO_AGENT);
// if(c == null) {
// c = new Contact(Config.LAMPIRO_AGENT, "Lampiro Agent", "none", null);
// subscribeContact(c);
// } else if(!"both".equals(c.subscription)) {
// // XXX resend presesence of type subscribe!
// }
// }
public Roster getRoster() {
return roster;
}
public void setXmppListener(XMPPClient.XmppListener xmppListener) {
this.xmppListener = xmppListener;
}
public XMPPClient.XmppListener getXmppListener() {
return xmppListener;
}
public static String getErrorString(String code) {
for (int i = 0; i < XMPPClient.errorCodes.length; i++) {
String[] ithCode = XMPPClient.errorCodes[i];
if (ithCode[0].equals(code)) { return (ithCode[1]); }
}
return "";
}
// #ifdef SEND_DEBUG1
public void sendDebug(String msg) {
Message m = new Message("ff@jabber.bluendo.com", "chat");
m.setBody(msg);
sendPacket(m);
}
// #endif
}