Home | History | Annotate | Download | only in smackx
      1 package org.jivesoftware.smackx;
      2 
      3 import java.util.HashMap;
      4 import java.util.Iterator;
      5 import java.util.List;
      6 import java.util.Map;
      7 
      8 import org.jivesoftware.smack.Connection;
      9 import org.jivesoftware.smack.PacketCollector;
     10 import org.jivesoftware.smack.PacketListener;
     11 import org.jivesoftware.smack.Roster;
     12 import org.jivesoftware.smack.RosterEntry;
     13 import org.jivesoftware.smack.SmackConfiguration;
     14 import org.jivesoftware.smack.XMPPException;
     15 import org.jivesoftware.smack.filter.PacketIDFilter;
     16 import org.jivesoftware.smack.filter.PacketTypeFilter;
     17 import org.jivesoftware.smack.packet.IQ;
     18 import org.jivesoftware.smack.packet.Packet;
     19 import org.jivesoftware.smack.packet.Presence;
     20 import org.jivesoftware.smack.packet.Registration;
     21 import org.jivesoftware.smack.util.StringUtils;
     22 import org.jivesoftware.smackx.packet.DiscoverInfo;
     23 import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
     24 
     25 /**
     26  * This class provides an abstract view to gateways/transports. This class handles all
     27  * actions regarding gateways and transports.
     28  * @author Till Klocke
     29  *
     30  */
     31 public class Gateway {
     32 
     33 	private Connection connection;
     34 	private ServiceDiscoveryManager sdManager;
     35 	private Roster roster;
     36 	private String entityJID;
     37 	private Registration registerInfo;
     38 	private Identity identity;
     39 	private DiscoverInfo info;
     40 
     41 	Gateway(Connection connection, String entityJID){
     42 		this.connection = connection;
     43 		this.roster = connection.getRoster();
     44 		this.sdManager = ServiceDiscoveryManager.getInstanceFor(connection);
     45 		this.entityJID = entityJID;
     46 	}
     47 
     48 	Gateway(Connection connection, String entityJID, DiscoverInfo info, Identity identity){
     49 		this(connection, entityJID);
     50 		this.info = info;
     51 		this.identity = identity;
     52 	}
     53 
     54 	private void discoverInfo() throws XMPPException{
     55 		info = sdManager.discoverInfo(entityJID);
     56 		Iterator<Identity> iterator = info.getIdentities();
     57 		while(iterator.hasNext()){
     58 			Identity temp = iterator.next();
     59 			if(temp.getCategory().equalsIgnoreCase("gateway")){
     60 				this.identity = temp;
     61 				break;
     62 			}
     63 		}
     64 	}
     65 
     66 	private Identity getIdentity() throws XMPPException{
     67 		if(identity==null){
     68 			discoverInfo();
     69 		}
     70 		return identity;
     71 	}
     72 
     73 	private Registration getRegisterInfo(){
     74 		if(registerInfo==null){
     75 			refreshRegisterInfo();
     76 		}
     77 		return registerInfo;
     78 	}
     79 
     80 	private void refreshRegisterInfo(){
     81 		Registration packet = new Registration();
     82 		packet.setFrom(connection.getUser());
     83 		packet.setType(IQ.Type.GET);
     84 		packet.setTo(entityJID);
     85 		PacketCollector collector =
     86 			connection.createPacketCollector(new PacketIDFilter(packet.getPacketID()));
     87 		connection.sendPacket(packet);
     88 		Packet result = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
     89 		collector.cancel();
     90 		if(result instanceof Registration && result.getError()==null){
     91 			Registration register = (Registration)result;
     92 			this.registerInfo = register;
     93 		}
     94 	}
     95 
     96 	/**
     97 	 * Checks if this gateway supports In-Band registration
     98 	 * @return true if In-Band registration is supported
     99 	 * @throws XMPPException
    100 	 */
    101 	public boolean canRegister() throws XMPPException{
    102 		if(info==null){
    103 			discoverInfo();
    104 		}
    105 		return info.containsFeature("jabber:iq:register");
    106 	}
    107 
    108 	/**
    109 	 * Returns all fields that are required to register to this gateway
    110 	 * @return a list of required fields
    111 	 */
    112 	public List<String> getRequiredFields(){
    113 		return getRegisterInfo().getRequiredFields();
    114 	}
    115 
    116 	/**
    117 	 * Returns the name as proposed in this gateways identity discovered via service
    118 	 * discovery
    119 	 * @return a String of its name
    120 	 * @throws XMPPException
    121 	 */
    122 	public String getName() throws XMPPException{
    123 		if(identity==null){
    124 			discoverInfo();
    125 		}
    126 		return identity.getName();
    127 	}
    128 
    129 	/**
    130 	 * Returns the type as proposed in this gateways identity discovered via service
    131 	 * discovery. See {@link http://xmpp.org/registrar/disco-categories.html} for
    132 	 * possible types
    133 	 * @return a String describing the type
    134 	 * @throws XMPPException
    135 	 */
    136 	public String getType() throws XMPPException{
    137 		if(identity==null){
    138 			discoverInfo();
    139 		}
    140 		return identity.getType();
    141 	}
    142 
    143 	/**
    144 	 * Returns true if the registration informations indicates that you are already
    145 	 * registered with this gateway
    146 	 * @return true if already registered
    147 	 * @throws XMPPException
    148 	 */
    149 	public boolean isRegistered() throws XMPPException{
    150 		return getRegisterInfo().isRegistered();
    151 	}
    152 
    153 	/**
    154 	 * Returns the value of specific field of the registration information. Can be used
    155 	 * to retrieve for example to retrieve username/password used on an already registered
    156 	 * gateway.
    157 	 * @param fieldName name of the field
    158 	 * @return a String containing the value of the field or null
    159 	 */
    160 	public String getField(String fieldName){
    161 		return getRegisterInfo().getField(fieldName);
    162 	}
    163 
    164 	/**
    165 	 * Returns a List of Strings of all field names which contain values.
    166 	 * @return a List of field names
    167 	 */
    168 	public List<String> getFieldNames(){
    169 		return getRegisterInfo().getFieldNames();
    170 	}
    171 
    172 	/**
    173 	 * A convenience method for retrieving the username of an existing account
    174 	 * @return String describing the username
    175 	 */
    176 	public String getUsername(){
    177 		return getField("username");
    178 	}
    179 
    180 	/**
    181 	 * A convenience method for retrieving the password of an existing accoung
    182 	 * @return String describing the password
    183 	 */
    184 	public String getPassword(){
    185 		return getField("password");
    186 	}
    187 
    188 	/**
    189 	 * Returns instructions for registering with this gateway
    190 	 * @return String containing instructions
    191 	 */
    192 	public String getInstructions(){
    193 		return getRegisterInfo().getInstructions();
    194 	}
    195 
    196 	/**
    197 	 * With this method you can register with this gateway or modify an existing registration
    198 	 * @param username String describing the username
    199 	 * @param password String describing the password
    200 	 * @param fields additional fields like email.
    201 	 * @throws XMPPException
    202 	 */
    203 	public void register(String username, String password, Map<String,String> fields)throws XMPPException{
    204 		if(getRegisterInfo().isRegistered()) {
    205 			throw new IllegalStateException("You are already registered with this gateway");
    206 		}
    207 		Registration register = new Registration();
    208 		register.setFrom(connection.getUser());
    209 		register.setTo(entityJID);
    210 		register.setType(IQ.Type.SET);
    211 		register.setUsername(username);
    212 		register.setPassword(password);
    213 		for(String s : fields.keySet()){
    214 			register.addAttribute(s, fields.get(s));
    215 		}
    216 		PacketCollector resultCollector =
    217 			connection.createPacketCollector(new PacketIDFilter(register.getPacketID()));
    218 		connection.sendPacket(register);
    219 		Packet result =
    220 			resultCollector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    221 		resultCollector.cancel();
    222 		if(result!=null && result instanceof IQ){
    223 			IQ resultIQ = (IQ)result;
    224 			if(resultIQ.getError()!=null){
    225 				throw new XMPPException(resultIQ.getError());
    226 			}
    227 			if(resultIQ.getType()==IQ.Type.ERROR){
    228 				throw new XMPPException(resultIQ.getError());
    229 			}
    230 			connection.addPacketListener(new GatewayPresenceListener(),
    231 					new PacketTypeFilter(Presence.class));
    232 			roster.createEntry(entityJID, getIdentity().getName(), new String[]{});
    233 		}
    234 		else{
    235 			throw new XMPPException("Packet reply timeout");
    236 		}
    237 	}
    238 
    239 	/**
    240 	 * A convenience method for registering or modifying an account on this gateway without
    241 	 * additional fields
    242 	 * @param username String describing the username
    243 	 * @param password String describing the password
    244 	 * @throws XMPPException
    245 	 */
    246 	public void register(String username, String password) throws XMPPException{
    247 		register(username, password,new HashMap<String,String>());
    248 	}
    249 
    250 	/**
    251 	 * This method removes an existing registration from this gateway
    252 	 * @throws XMPPException
    253 	 */
    254 	public void unregister() throws XMPPException{
    255 		Registration register = new Registration();
    256 		register.setFrom(connection.getUser());
    257 		register.setTo(entityJID);
    258 		register.setType(IQ.Type.SET);
    259 		register.setRemove(true);
    260 		PacketCollector resultCollector =
    261 			connection.createPacketCollector(new PacketIDFilter(register.getPacketID()));
    262 		connection.sendPacket(register);
    263 		Packet result = resultCollector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    264 		resultCollector.cancel();
    265 		if(result!=null && result instanceof IQ){
    266 			IQ resultIQ = (IQ)result;
    267 			if(resultIQ.getError()!=null){
    268 				throw new XMPPException(resultIQ.getError());
    269 			}
    270 			if(resultIQ.getType()==IQ.Type.ERROR){
    271 				throw new XMPPException(resultIQ.getError());
    272 			}
    273 			RosterEntry gatewayEntry = roster.getEntry(entityJID);
    274 			roster.removeEntry(gatewayEntry);
    275 		}
    276 		else{
    277 			throw new XMPPException("Packet reply timeout");
    278 		}
    279 	}
    280 
    281 	/**
    282 	 * Lets you login manually in this gateway. Normally a gateway logins you when it
    283 	 * receives the first presence broadcasted by your server. But it is possible to
    284 	 * manually login and logout by sending a directed presence. This method sends an
    285 	 * empty available presence direct to the gateway.
    286 	 */
    287 	public void login(){
    288 		Presence presence = new Presence(Presence.Type.available);
    289 		login(presence);
    290 	}
    291 
    292 	/**
    293 	 * This method lets you send the presence direct to the gateway. Type, To and From
    294 	 * are modified.
    295 	 * @param presence the presence used to login to gateway
    296 	 */
    297 	public void login(Presence presence){
    298 		presence.setType(Presence.Type.available);
    299 		presence.setTo(entityJID);
    300 		presence.setFrom(connection.getUser());
    301 		connection.sendPacket(presence);
    302 	}
    303 
    304 	/**
    305 	 * This method logs you out from this gateway by sending an unavailable presence
    306 	 * to directly to this gateway.
    307 	 */
    308 	public void logout(){
    309 		Presence presence = new Presence(Presence.Type.unavailable);
    310 		presence.setTo(entityJID);
    311 		presence.setFrom(connection.getUser());
    312 		connection.sendPacket(presence);
    313 	}
    314 
    315 	private class GatewayPresenceListener implements PacketListener{
    316 
    317 		public void processPacket(Packet packet) {
    318 			if(packet instanceof Presence){
    319 				Presence presence = (Presence)packet;
    320 				if(entityJID.equals(presence.getFrom()) &&
    321 						roster.contains(presence.getFrom()) &&
    322 						presence.getType().equals(Presence.Type.subscribe)){
    323 					Presence response = new Presence(Presence.Type.subscribed);
    324 					response.setTo(presence.getFrom());
    325 					response.setFrom(StringUtils.parseBareAddress(connection.getUser()));
    326 					connection.sendPacket(response);
    327 				}
    328 			}
    329 
    330 		}
    331 	}
    332 
    333 }
    334