blob: 5b5836fc50f1efefb1b3317de3034f89d170ee0f [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001package org.jivesoftware.smackx;
2
3import java.util.HashMap;
4import java.util.Iterator;
5import java.util.List;
6import java.util.Map;
7
8import org.jivesoftware.smack.Connection;
9import org.jivesoftware.smack.PacketCollector;
10import org.jivesoftware.smack.PacketListener;
11import org.jivesoftware.smack.Roster;
12import org.jivesoftware.smack.RosterEntry;
13import org.jivesoftware.smack.SmackConfiguration;
14import org.jivesoftware.smack.XMPPException;
15import org.jivesoftware.smack.filter.PacketIDFilter;
16import org.jivesoftware.smack.filter.PacketTypeFilter;
17import org.jivesoftware.smack.packet.IQ;
18import org.jivesoftware.smack.packet.Packet;
19import org.jivesoftware.smack.packet.Presence;
20import org.jivesoftware.smack.packet.Registration;
21import org.jivesoftware.smack.util.StringUtils;
22import org.jivesoftware.smackx.packet.DiscoverInfo;
23import 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 */
31public 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}