blob: 4d9faa591a72dfc33137f3b12179d59569f995a2 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smack;
22
23import org.jivesoftware.smack.filter.AndFilter;
24import org.jivesoftware.smack.filter.PacketFilter;
25import org.jivesoftware.smack.filter.PacketIDFilter;
26import org.jivesoftware.smack.filter.PacketTypeFilter;
27import org.jivesoftware.smack.packet.IQ;
28import org.jivesoftware.smack.packet.Registration;
29import org.jivesoftware.smack.util.StringUtils;
30
31import java.util.Collection;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.List;
36import java.util.Map;
37import java.util.Set;
38
39/**
40 * Allows creation and management of accounts on an XMPP server.
41 *
42 * @see Connection#getAccountManager()
43 * @author Matt Tucker
44 */
45public class AccountManager {
46
47 private Connection connection;
48 private Registration info = null;
49
50 /**
51 * Flag that indicates whether the server supports In-Band Registration.
52 * In-Band Registration may be advertised as a stream feature. If no stream feature
53 * was advertised from the server then try sending an IQ packet to discover if In-Band
54 * Registration is available.
55 */
56 private boolean accountCreationSupported = false;
57
58 /**
59 * Creates a new AccountManager instance.
60 *
61 * @param connection a connection to a XMPP server.
62 */
63 public AccountManager(Connection connection) {
64 this.connection = connection;
65 }
66
67 /**
68 * Sets whether the server supports In-Band Registration. In-Band Registration may be
69 * advertised as a stream feature. If no stream feature was advertised from the server
70 * then try sending an IQ packet to discover if In-Band Registration is available.
71 *
72 * @param accountCreationSupported true if the server supports In-Band Registration.
73 */
74 void setSupportsAccountCreation(boolean accountCreationSupported) {
75 this.accountCreationSupported = accountCreationSupported;
76 }
77
78 /**
79 * Returns true if the server supports creating new accounts. Many servers require
80 * that you not be currently authenticated when creating new accounts, so the safest
81 * behavior is to only create new accounts before having logged in to a server.
82 *
83 * @return true if the server support creating new accounts.
84 */
85 public boolean supportsAccountCreation() {
86 // Check if we already know that the server supports creating new accounts
87 if (accountCreationSupported) {
88 return true;
89 }
90 // No information is known yet (e.g. no stream feature was received from the server
91 // indicating that it supports creating new accounts) so send an IQ packet as a way
92 // to discover if this feature is supported
93 try {
94 if (info == null) {
95 getRegistrationInfo();
96 accountCreationSupported = info.getType() != IQ.Type.ERROR;
97 }
98 return accountCreationSupported;
99 }
100 catch (XMPPException xe) {
101 return false;
102 }
103 }
104
105 /**
106 * Returns an unmodifiable collection of the names of the required account attributes.
107 * All attributes must be set when creating new accounts. The standard set of possible
108 * attributes are as follows: <ul>
109 * <li>name -- the user's name.
110 * <li>first -- the user's first name.
111 * <li>last -- the user's last name.
112 * <li>email -- the user's email address.
113 * <li>city -- the user's city.
114 * <li>state -- the user's state.
115 * <li>zip -- the user's ZIP code.
116 * <li>phone -- the user's phone number.
117 * <li>url -- the user's website.
118 * <li>date -- the date the registration took place.
119 * <li>misc -- other miscellaneous information to associate with the account.
120 * <li>text -- textual information to associate with the account.
121 * <li>remove -- empty flag to remove account.
122 * </ul><p>
123 *
124 * Typically, servers require no attributes when creating new accounts, or just
125 * the user's email address.
126 *
127 * @return the required account attributes.
128 */
129 public Collection<String> getAccountAttributes() {
130 try {
131 if (info == null) {
132 getRegistrationInfo();
133 }
134 Map<String, String> attributes = info.getAttributes();
135 if (attributes != null) {
136 return Collections.unmodifiableSet(attributes.keySet());
137 }
138 }
139 catch (XMPPException xe) {
140 xe.printStackTrace();
141 }
142 return Collections.emptySet();
143 }
144
145 /**
146 * Returns the value of a given account attribute or <tt>null</tt> if the account
147 * attribute wasn't found.
148 *
149 * @param name the name of the account attribute to return its value.
150 * @return the value of the account attribute or <tt>null</tt> if an account
151 * attribute wasn't found for the requested name.
152 */
153 public String getAccountAttribute(String name) {
154 try {
155 if (info == null) {
156 getRegistrationInfo();
157 }
158 return info.getAttributes().get(name);
159 }
160 catch (XMPPException xe) {
161 xe.printStackTrace();
162 }
163 return null;
164 }
165
166 /**
167 * Returns the instructions for creating a new account, or <tt>null</tt> if there
168 * are no instructions. If present, instructions should be displayed to the end-user
169 * that will complete the registration process.
170 *
171 * @return the account creation instructions, or <tt>null</tt> if there are none.
172 */
173 public String getAccountInstructions() {
174 try {
175 if (info == null) {
176 getRegistrationInfo();
177 }
178 return info.getInstructions();
179 }
180 catch (XMPPException xe) {
181 return null;
182 }
183 }
184
185 /**
186 * Creates a new account using the specified username and password. The server may
187 * require a number of extra account attributes such as an email address and phone
188 * number. In that case, Smack will attempt to automatically set all required
189 * attributes with blank values, which may or may not be accepted by the server.
190 * Therefore, it's recommended to check the required account attributes and to let
191 * the end-user populate them with real values instead.
192 *
193 * @param username the username.
194 * @param password the password.
195 * @throws XMPPException if an error occurs creating the account.
196 */
197 public void createAccount(String username, String password) throws XMPPException {
198 if (!supportsAccountCreation()) {
199 throw new XMPPException("Server does not support account creation.");
200 }
201 // Create a map for all the required attributes, but give them blank values.
202 Map<String, String> attributes = new HashMap<String, String>();
203 for (String attributeName : getAccountAttributes()) {
204 attributes.put(attributeName, "");
205 }
206 createAccount(username, password, attributes);
207 }
208
209 /**
210 * Creates a new account using the specified username, password and account attributes.
211 * The attributes Map must contain only String name/value pairs and must also have values
212 * for all required attributes.
213 *
214 * @param username the username.
215 * @param password the password.
216 * @param attributes the account attributes.
217 * @throws XMPPException if an error occurs creating the account.
218 * @see #getAccountAttributes()
219 */
220 public void createAccount(String username, String password, Map<String, String> attributes)
221 throws XMPPException
222 {
223 if (!supportsAccountCreation()) {
224 throw new XMPPException("Server does not support account creation.");
225 }
226 Registration reg = new Registration();
227 reg.setType(IQ.Type.SET);
228 reg.setTo(connection.getServiceName());
229 attributes.put("username",username);
230 attributes.put("password",password);
231 reg.setAttributes(attributes);
232 PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
233 new PacketTypeFilter(IQ.class));
234 PacketCollector collector = connection.createPacketCollector(filter);
235 connection.sendPacket(reg);
236 IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
237 // Stop queuing results
238 collector.cancel();
239 if (result == null) {
240 throw new XMPPException("No response from server.");
241 }
242 else if (result.getType() == IQ.Type.ERROR) {
243 throw new XMPPException(result.getError());
244 }
245 }
246
247 /**
248 * Changes the password of the currently logged-in account. This operation can only
249 * be performed after a successful login operation has been completed. Not all servers
250 * support changing passwords; an XMPPException will be thrown when that is the case.
251 *
252 * @throws IllegalStateException if not currently logged-in to the server.
253 * @throws XMPPException if an error occurs when changing the password.
254 */
255 public void changePassword(String newPassword) throws XMPPException {
256 Registration reg = new Registration();
257 reg.setType(IQ.Type.SET);
258 reg.setTo(connection.getServiceName());
259 Map<String, String> map = new HashMap<String, String>();
260 map.put("username",StringUtils.parseName(connection.getUser()));
261 map.put("password",newPassword);
262 reg.setAttributes(map);
263 PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
264 new PacketTypeFilter(IQ.class));
265 PacketCollector collector = connection.createPacketCollector(filter);
266 connection.sendPacket(reg);
267 IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
268 // Stop queuing results
269 collector.cancel();
270 if (result == null) {
271 throw new XMPPException("No response from server.");
272 }
273 else if (result.getType() == IQ.Type.ERROR) {
274 throw new XMPPException(result.getError());
275 }
276 }
277
278 /**
279 * Deletes the currently logged-in account from the server. This operation can only
280 * be performed after a successful login operation has been completed. Not all servers
281 * support deleting accounts; an XMPPException will be thrown when that is the case.
282 *
283 * @throws IllegalStateException if not currently logged-in to the server.
284 * @throws XMPPException if an error occurs when deleting the account.
285 */
286 public void deleteAccount() throws XMPPException {
287 if (!connection.isAuthenticated()) {
288 throw new IllegalStateException("Must be logged in to delete a account.");
289 }
290 Registration reg = new Registration();
291 reg.setType(IQ.Type.SET);
292 reg.setTo(connection.getServiceName());
293 Map<String, String> attributes = new HashMap<String, String>();
294 // To delete an account, we add a single attribute, "remove", that is blank.
295 attributes.put("remove", "");
296 reg.setAttributes(attributes);
297 PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
298 new PacketTypeFilter(IQ.class));
299 PacketCollector collector = connection.createPacketCollector(filter);
300 connection.sendPacket(reg);
301 IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
302 // Stop queuing results
303 collector.cancel();
304 if (result == null) {
305 throw new XMPPException("No response from server.");
306 }
307 else if (result.getType() == IQ.Type.ERROR) {
308 throw new XMPPException(result.getError());
309 }
310 }
311
312 /**
313 * Gets the account registration info from the server.
314 *
315 * @throws XMPPException if an error occurs.
316 */
317 private synchronized void getRegistrationInfo() throws XMPPException {
318 Registration reg = new Registration();
319 reg.setTo(connection.getServiceName());
320 PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
321 new PacketTypeFilter(IQ.class));
322 PacketCollector collector = connection.createPacketCollector(filter);
323 connection.sendPacket(reg);
324 IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
325 // Stop queuing results
326 collector.cancel();
327 if (result == null) {
328 throw new XMPPException("No response from server.");
329 }
330 else if (result.getType() == IQ.Type.ERROR) {
331 throw new XMPPException(result.getError());
332 }
333 else {
334 info = (Registration)result;
335 }
336 }
337}