• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Gateway(Connection connection, String entityJID)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 
Gateway(Connection connection, String entityJID, DiscoverInfo info, Identity identity)48 	Gateway(Connection connection, String entityJID, DiscoverInfo info, Identity identity){
49 		this(connection, entityJID);
50 		this.info = info;
51 		this.identity = identity;
52 	}
53 
discoverInfo()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 
getIdentity()66 	private Identity getIdentity() throws XMPPException{
67 		if(identity==null){
68 			discoverInfo();
69 		}
70 		return identity;
71 	}
72 
getRegisterInfo()73 	private Registration getRegisterInfo(){
74 		if(registerInfo==null){
75 			refreshRegisterInfo();
76 		}
77 		return registerInfo;
78 	}
79 
refreshRegisterInfo()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 	 */
canRegister()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 	 */
getRequiredFields()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 	 */
getName()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 	 */
getType()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 	 */
isRegistered()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 	 */
getField(String fieldName)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 	 */
getFieldNames()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 	 */
getUsername()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 	 */
getPassword()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 	 */
getInstructions()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 	 */
register(String username, String password, Map<String,String> fields)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 	 */
register(String username, String password)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 	 */
unregister()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 	 */
login()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 	 */
login(Presence presence)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 	 */
logout()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 
processPacket(Packet packet)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