package tigase.xmpp.impl; import java.util.Map; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; import tigase.db.NonAuthUserRepository; import tigase.db.TigaseDBException; import tigase.db.UserNotFoundException; import tigase.server.Packet; import tigase.xml.Element; import tigase.xmpp.Authorization; import tigase.xmpp.BareJID; import tigase.xmpp.JID; import tigase.xmpp.NotAuthorizedException; import tigase.xmpp.PacketErrorTypeException; import tigase.xmpp.StanzaType; import tigase.xmpp.XMPPException; import tigase.xmpp.XMPPProcessorAbstract; import tigase.xmpp.XMPPResourceConnection; import tigase.xmpp.XMPPStopListenerIfc; /** * Implementation of <a * href='http://xmpp.org/extensions/xep-0012.html'>XEP-0012</a>: Last Activity. * * @author bmalkow * */ public abstract class LastActivity extends XMPPProcessorAbstract implements XMPPStopListenerIfc { private static final Element[] DISCO_FEATURES = { new Element("feature", new String[] { "var" }, new String[] { "jabber:iq:last" }) }; private static final String[] ELEMENTS = { "query", "presence", "message" }; private final static String ID = "jabber:iq:last"; private final static String LAST_ACTIVITY_KEY = "LAST_ACTIVITY_KEY"; private static final Logger log = Logger.getLogger(LastActivity.class.getName()); private final static String[] XMLNS = new String[] { "jabber:iq:last", "jabber:client", "jabber:client" }; private static final long getTime(NonAuthUserRepository repo, BareJID requestedJid) throws UserNotFoundException { String data = repo.getPublicData(requestedJid, ID, LAST_ACTIVITY_KEY, null); if (data == null) return -1; try { return Long.parseLong(data); } catch (Exception e) { return -1; } } private static final long getTime(XMPPResourceConnection session) { final Long last = (Long) session.getSessionData(LAST_ACTIVITY_KEY); return last == null ? -1 : last.longValue(); } @Override public String id() { return ID; } @Override public void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings) throws XMPPException { if (packet.getElemName() != "iq" && session != null && session.getBareJID().equals(packet.getStanzaFrom().getBareJID())) { final long time = System.currentTimeMillis(); if (log.isLoggable(Level.FINEST)) log.finest("Updating last:activity of user " + session.getUserName() + " to " + time); session.putSessionData(LAST_ACTIVITY_KEY, time); } super.process(packet, session, repo, results, settings); } /* * User odpytuje sam siebie */ @Override public void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings) throws PacketErrorTypeException { if (packet.getType() == StanzaType.get) { long last = getTime(session); Packet resp = packet.okResult((Element) null, 0); if (last != -1) { long result = (System.currentTimeMillis() - last) / 1000; Element q = new Element("query", new String[] { "xmlns", "seconds" }, new String[] { "jabber:iq:last", "" + result }); resp.getElement().addChild(q); results.offer(resp); } else { results.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, "Unknown last activity time", true)); } } else results.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, "Message type is incorrect", true)); } /* * User docelowy jest offline */ @Override public void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings) throws PacketErrorTypeException { if (packet.getType() == StanzaType.get) { try { BareJID requestedJid = packet.getStanzaTo().getBareJID(); final long last = getTime(repo, requestedJid); if (log.isLoggable(Level.FINEST)) log.finest("Get last:activity of offline user " + requestedJid + ". value=" + last); if (last != -1) { long result = (System.currentTimeMillis() - last) / 1000; Packet resp = packet.okResult((Element) null, 0); Element q = new Element("query", new String[] { "xmlns", "seconds" }, new String[] { "jabber:iq:last", "" + result }); resp.getElement().addChild(q); results.offer(resp); } else { results.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, "Unknown last activity time", true)); } } catch (UserNotFoundException e) { results.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, "User not found", true)); } } else if (packet.getType() == StanzaType.set) results.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, "Message type is incorrect", true)); else super.processNullSessionPacket(packet, repo, results, settings); } @Override public void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings) throws PacketErrorTypeException { } /* * User docelowy jest podłączony */ @Override public void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings) throws PacketErrorTypeException { if (packet.getType() == StanzaType.get) { long last = getTime(session); if (last != -1) { long result = (System.currentTimeMillis() - last) / 1000; Packet resp = packet.okResult((Element) null, 0); Element q = new Element("query", new String[] { "xmlns", "seconds" }, new String[] { "jabber:iq:last", "" + result }); resp.getElement().addChild(q); results.offer(resp); } else { results.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, "Unknown last activity time", true)); } } else if (packet.getType() == StanzaType.set) results.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, "Message type is incorrect", true)); else super.processToUserPacket(packet, session, repo, results, settings); } @Override public void stopped(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings) { if (session != null) { long last = getTime(session); try { if (log.isLoggable(Level.FINEST)) log.finest("Persiting last:activity of user " + session.getUserName() + " in storage (value=" + last + ")."); session.setPublicData(ID, LAST_ACTIVITY_KEY, String.valueOf(last)); } catch (NotAuthorizedException e) { e.printStackTrace(); } catch (TigaseDBException e) { e.printStackTrace(); } } } @Override public Element[] supDiscoFeatures(XMPPResourceConnection session) { return DISCO_FEATURES; } @Override public String[] supElements() { return ELEMENTS; } @Override public String[] supNamespaces() { return XMLNS; } }