package org.jivesoftware.smackx; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.PacketCollector; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Registration; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.packet.DiscoverInfo; import org.jivesoftware.smackx.packet.DiscoverInfo.Identity; /** * This class provides an abstract view to gateways/transports. This class handles all * actions regarding gateways and transports. * @author Till Klocke * */ public class Gateway { private Connection connection; private ServiceDiscoveryManager sdManager; private Roster roster; private String entityJID; private Registration registerInfo; private Identity identity; private DiscoverInfo info; Gateway(Connection connection, String entityJID){ this.connection = connection; this.roster = connection.getRoster(); this.sdManager = ServiceDiscoveryManager.getInstanceFor(connection); this.entityJID = entityJID; } Gateway(Connection connection, String entityJID, DiscoverInfo info, Identity identity){ this(connection, entityJID); this.info = info; this.identity = identity; } private void discoverInfo() throws XMPPException{ info = sdManager.discoverInfo(entityJID); Iterator<Identity> iterator = info.getIdentities(); while(iterator.hasNext()){ Identity temp = iterator.next(); if(temp.getCategory().equalsIgnoreCase("gateway")){ this.identity = temp; break; } } } private Identity getIdentity() throws XMPPException{ if(identity==null){ discoverInfo(); } return identity; } private Registration getRegisterInfo(){ if(registerInfo==null){ refreshRegisterInfo(); } return registerInfo; } private void refreshRegisterInfo(){ Registration packet = new Registration(); packet.setFrom(connection.getUser()); packet.setType(IQ.Type.GET); packet.setTo(entityJID); PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(packet.getPacketID())); connection.sendPacket(packet); Packet result = collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); collector.cancel(); if(result instanceof Registration && result.getError()==null){ Registration register = (Registration)result; this.registerInfo = register; } } /** * Checks if this gateway supports In-Band registration * @return true if In-Band registration is supported * @throws XMPPException */ public boolean canRegister() throws XMPPException{ if(info==null){ discoverInfo(); } return info.containsFeature("jabber:iq:register"); } /** * Returns all fields that are required to register to this gateway * @return a list of required fields */ public List<String> getRequiredFields(){ return getRegisterInfo().getRequiredFields(); } /** * Returns the name as proposed in this gateways identity discovered via service * discovery * @return a String of its name * @throws XMPPException */ public String getName() throws XMPPException{ if(identity==null){ discoverInfo(); } return identity.getName(); } /** * Returns the type as proposed in this gateways identity discovered via service * discovery. See {@link http://xmpp.org/registrar/disco-categories.html} for * possible types * @return a String describing the type * @throws XMPPException */ public String getType() throws XMPPException{ if(identity==null){ discoverInfo(); } return identity.getType(); } /** * Returns true if the registration informations indicates that you are already * registered with this gateway * @return true if already registered * @throws XMPPException */ public boolean isRegistered() throws XMPPException{ return getRegisterInfo().isRegistered(); } /** * Returns the value of specific field of the registration information. Can be used * to retrieve for example to retrieve username/password used on an already registered * gateway. * @param fieldName name of the field * @return a String containing the value of the field or null */ public String getField(String fieldName){ return getRegisterInfo().getField(fieldName); } /** * Returns a List of Strings of all field names which contain values. * @return a List of field names */ public List<String> getFieldNames(){ return getRegisterInfo().getFieldNames(); } /** * A convenience method for retrieving the username of an existing account * @return String describing the username */ public String getUsername(){ return getField("username"); } /** * A convenience method for retrieving the password of an existing accoung * @return String describing the password */ public String getPassword(){ return getField("password"); } /** * Returns instructions for registering with this gateway * @return String containing instructions */ public String getInstructions(){ return getRegisterInfo().getInstructions(); } /** * With this method you can register with this gateway or modify an existing registration * @param username String describing the username * @param password String describing the password * @param fields additional fields like email. * @throws XMPPException */ public void register(String username, String password, Map<String,String> fields)throws XMPPException{ if(getRegisterInfo().isRegistered()) { throw new IllegalStateException("You are already registered with this gateway"); } Registration register = new Registration(); register.setFrom(connection.getUser()); register.setTo(entityJID); register.setType(IQ.Type.SET); register.setUsername(username); register.setPassword(password); for(String s : fields.keySet()){ register.addAttribute(s, fields.get(s)); } PacketCollector resultCollector = connection.createPacketCollector(new PacketIDFilter(register.getPacketID())); connection.sendPacket(register); Packet result = resultCollector.nextResult(SmackConfiguration.getPacketReplyTimeout()); resultCollector.cancel(); if(result!=null && result instanceof IQ){ IQ resultIQ = (IQ)result; if(resultIQ.getError()!=null){ throw new XMPPException(resultIQ.getError()); } if(resultIQ.getType()==IQ.Type.ERROR){ throw new XMPPException(resultIQ.getError()); } connection.addPacketListener(new GatewayPresenceListener(), new PacketTypeFilter(Presence.class)); roster.createEntry(entityJID, getIdentity().getName(), new String[]{}); } else{ throw new XMPPException("Packet reply timeout"); } } /** * A convenience method for registering or modifying an account on this gateway without * additional fields * @param username String describing the username * @param password String describing the password * @throws XMPPException */ public void register(String username, String password) throws XMPPException{ register(username, password,new HashMap<String,String>()); } /** * This method removes an existing registration from this gateway * @throws XMPPException */ public void unregister() throws XMPPException{ Registration register = new Registration(); register.setFrom(connection.getUser()); register.setTo(entityJID); register.setType(IQ.Type.SET); register.setRemove(true); PacketCollector resultCollector = connection.createPacketCollector(new PacketIDFilter(register.getPacketID())); connection.sendPacket(register); Packet result = resultCollector.nextResult(SmackConfiguration.getPacketReplyTimeout()); resultCollector.cancel(); if(result!=null && result instanceof IQ){ IQ resultIQ = (IQ)result; if(resultIQ.getError()!=null){ throw new XMPPException(resultIQ.getError()); } if(resultIQ.getType()==IQ.Type.ERROR){ throw new XMPPException(resultIQ.getError()); } RosterEntry gatewayEntry = roster.getEntry(entityJID); roster.removeEntry(gatewayEntry); } else{ throw new XMPPException("Packet reply timeout"); } } /** * Lets you login manually in this gateway. Normally a gateway logins you when it * receives the first presence broadcasted by your server. But it is possible to * manually login and logout by sending a directed presence. This method sends an * empty available presence direct to the gateway. */ public void login(){ Presence presence = new Presence(Presence.Type.available); login(presence); } /** * This method lets you send the presence direct to the gateway. Type, To and From * are modified. * @param presence the presence used to login to gateway */ public void login(Presence presence){ presence.setType(Presence.Type.available); presence.setTo(entityJID); presence.setFrom(connection.getUser()); connection.sendPacket(presence); } /** * This method logs you out from this gateway by sending an unavailable presence * to directly to this gateway. */ public void logout(){ Presence presence = new Presence(Presence.Type.unavailable); presence.setTo(entityJID); presence.setFrom(connection.getUser()); connection.sendPacket(presence); } private class GatewayPresenceListener implements PacketListener{ public void processPacket(Packet packet) { if(packet instanceof Presence){ Presence presence = (Presence)packet; if(entityJID.equals(presence.getFrom()) && roster.contains(presence.getFrom()) && presence.getType().equals(Presence.Type.subscribe)){ Presence response = new Presence(Presence.Type.subscribed); response.setTo(presence.getFrom()); response.setFrom(StringUtils.parseBareAddress(connection.getUser())); connection.sendPacket(response); } } } } }