Initial account manager functionality.

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@1846 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Matt Tucker 2003-03-08 23:09:48 +00:00 committed by mtucker
parent 996f50449c
commit 7fa5b3608e
6 changed files with 362 additions and 31 deletions

View File

@ -0,0 +1,109 @@
package org.jivesoftware.smack;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.filter.*;
import java.util.*;
/**
* Allows creation and management of accounts on an XMPP server.
*
* @see XMPPConnection#getAccountManager();
* @author Matt Tucker
*/
public class AccountManager {
private XMPPConnection connection;
private Registration info = null;
public AccountManager(XMPPConnection connection) {
this.connection = connection;
}
/**
* Returns true if the server supports creating new accounts. Many servers require that
* you not be currently authenticated when creating new accounts, so the safest
* behavior is to only create new accounts when
*
* @return true if the server support creating new accounts.
*/
public boolean supportsAccountCreation() {
try {
if (info == null) {
getRegistrationInfo();
}
return info.getType() != IQ.Type.ERROR;
}
catch (XMPPException xe) {
return false;
}
}
public Iterator getAccountAttributes() {
try {
if (info == null) {
getRegistrationInfo();
}
Map attributes = info.getAttributes();
if (attributes != null) {
return attributes.keySet().iterator();
}
}
catch (XMPPException xe) { }
return Collections.EMPTY_LIST.iterator();
}
public void createAccount(String username, String password) throws XMPPException {
if (!supportsAccountCreation()) {
throw new XMPPException("Server does not support account creation.");
}
}
public void createAccount(String username, String password, Map attributes)
throws XMPPException
{
}
public void deleteAccount() throws XMPPException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Must be logged in to delete a account.");
}
Registration reg = new Registration();
reg.setType(IQ.Type.SET);
Map attributes = new HashMap();
// To delete an account, we add a single attribute, "remove", that is blank.
attributes.put("remove", "");
reg.setAttributes(attributes);
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
new PacketTypeFilter(Registration.class));
PacketCollector collector = connection.createPacketCollector(filter);
connection.sendPacket(reg);
IQ result = (IQ)collector.nextResult(5000);
if (result == null) {
throw new XMPPException("No response from server.");
}
else if (result.getType() == IQ.Type.ERROR) {
throw new XMPPException(result.getError());
}
}
private synchronized void getRegistrationInfo() throws XMPPException {
Registration reg = new Registration();
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
new PacketTypeFilter(IQ.class));
PacketCollector collector = connection.createPacketCollector(filter);
connection.sendPacket(reg);
IQ result = (IQ)collector.nextResult(5000);
if (result == null) {
throw new XMPPException("No response from server.");
}
else if (result.getType() == IQ.Type.ERROR) {
throw new XMPPException(result.getError());
}
else {
info = (Registration)result;
}
}
}

View File

@ -342,6 +342,9 @@ class PacketReader {
else if (namespace.equals("jabber:iq:roster")) {
iqPacket = parseRoster(parser);
}
else if (namespace.equals("jabber:iq:register")) {
iqPacket = parseRegistration(parser);
}
}
else if (parser.getName().equals("error")) {
error = parseError(parser);
@ -437,6 +440,41 @@ class PacketReader {
return roster;
}
private static Registration parseRegistration(XmlPullParser parser) throws Exception {
Registration registration = new Registration();
Map fields = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == parser.START_TAG) {
if (parser.getName().equals("username")) {
registration.setUsername(parser.nextText());
}
else if (parser.getName().equals("password")) {
registration.setPassword(parser.nextText());
}
else {
String name = parser.getName();
String value = parser.nextText();
// Ignore instructions, but anything else should be added to the map.
if (!name.equals("instructions")) {
if (fields == null) {
fields = new HashMap();
}
fields.put(name, value);
}
}
}
else if (eventType == parser.END_TAG) {
if (parser.getName().equals("query")) {
done = true;
}
}
}
registration.setAttributes(fields);
return registration;
}
private static XMPPError parseError(XmlPullParser parser) throws Exception {
String errorCode = null;
for (int i=0; i<parser.getAttributeCount(); i++) {
@ -449,7 +487,6 @@ class PacketReader {
if (parser.getEventType() == parser.END_TAG && parser.getName().equals("error")) {
break;
}
parser.next();
}
return new XMPPError(Integer.parseInt(errorCode), message);
}
@ -493,6 +530,9 @@ class PacketReader {
thread = parser.nextText();
}
}
else if (parser.getName().equals("error")) {
message.setError(parseError(parser));
}
else if (parser.getName().equals("x") &&
parser.getNamespace().equals(PROPERTIES_NAMESPACE))
{
@ -557,6 +597,9 @@ class PacketReader {
else if (parser.getName().equals("show")) {
presence.setMode(Presence.Mode.fromString(parser.nextText()));
}
else if (parser.getName().equals("error")) {
presence.setError(parseError(parser));
}
else if (parser.getName().equals("x") &&
parser.getNamespace().equals(PROPERTIES_NAMESPACE))
{

View File

@ -53,7 +53,6 @@
package org.jivesoftware.smack;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketFilter;
@ -71,13 +70,10 @@ import java.awt.*;
* // Most servers require you to login before performing other tasks.
* con.login("jsmith", "mypass");
* // Start a new conversation with John Doe and send him a message.
* Chat chat = new Chat("jdoe@jabber.org");
* Chat chat = con.createChat("jdoe@jabber.org");
* chat.sendMessage("Hey, how's it going?");
* </pre>
*
* Every connection has a PacketReader and PacketWriter instance, which are used
* to read and write XML with the server.
*
* @author Matt Tucker
*/
public class XMPPConnection {
@ -106,11 +102,13 @@ public class XMPPConnection {
String connectionID;
private boolean connected = false;
private boolean authenticated = false;
private PacketWriter packetWriter;
private PacketReader packetReader;
private Roster roster;
private Roster roster = null;
private AccountManager accountManager = null;
Writer writer;
Reader reader;
@ -209,6 +207,8 @@ public class XMPPConnection {
* @param password the password.
* @param resource the resource.
* @throws XMPPException if an error occurs.
* @throws IllegalStateException if not connected to the server, or already logged in
* to the serrver.
*/
public synchronized void login(String username, String password, String resource)
throws XMPPException
@ -216,6 +216,9 @@ public class XMPPConnection {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
}
// If we send an authentication packet in "get" mode with just the username,
// the server will return the list of authentication protocols it supports.
Authentication discoveryAuth = new Authentication();
@ -263,17 +266,7 @@ public class XMPPConnection {
throw new XMPPException("Authentication failed.");
}
else if (response.getType() == IQ.Type.ERROR) {
if (response.getError() == null) {
throw new XMPPException("Authentication failed.");
}
else {
XMPPError error = response.getError();
String msg = "Authentication failed -- " + error.getCode();
if (error.getMessage() != null) {
msg += ": " + error.getMessage();
}
throw new XMPPException(msg);
}
throw new XMPPException(response.getError());
}
// We're done with the collector, so explicitly cancel it.
collector.cancel();
@ -284,6 +277,9 @@ public class XMPPConnection {
this.roster = new Roster(this);
roster.reload();
// Indicate that we're now authenticated.
authenticated = true;
// If debugging is enabled, change the the debug window title to include the
// name we are now logged-in as.
if (DEBUG_ENABLED) {
@ -295,10 +291,27 @@ public class XMPPConnection {
}
}
/**
* Returns the roster for the user logged into the server. If the user has not yet
* logged into the server, this method will return <tt>null</tt>.
*
* @return the user's roster, or <tt>null</tt> if the user has not logged in yet.
*/
public Roster getRoster() {
return roster;
}
/**
* Returns an account manager instance for this
* @return
*/
public synchronized AccountManager getAccountManager() {
if (accountManager == null) {
accountManager = new AccountManager(this);
}
return accountManager;
}
/**
* Creates a new chat with the specified participant. The participant should
* be a valid Jabber user such as <tt>jdoe@jivesoftware.com</tt> or
@ -329,7 +342,7 @@ public class XMPPConnection {
}
/**
* Returns true if currently connected to the Jabber server.
* Returns true if currently connected to the XMPP server.
*
* @return true if connected.
*/
@ -337,6 +350,15 @@ public class XMPPConnection {
return connected;
}
/**
* Returns true if currently authenticated by successfully calling the login method.
*
* @return true if authenticated.
*/
public boolean isAuthenticated() {
return authenticated;
}
/**
* Closes the connection by setting presence to unavailable then closing the stream to
* the XMPP server. Once a connection has been closed, it cannot be re-opened.
@ -355,6 +377,7 @@ public class XMPPConnection {
socket.close();
}
catch (Exception e) { }
authenticated = false;
connected = false;
}

View File

@ -97,19 +97,12 @@ public class XMPPException extends Exception {
}
public void printStackTrace() {
if (error != null) {
System.err.println(error);
}
super.printStackTrace();
if (cause != null) {
System.err.println("Nested Exception: ");
cause.printStackTrace();
}
printStackTrace(System.err);
}
public void printStackTrace(PrintStream out) {
if (error != null) {
System.err.println(error);
System.err.print(error + " -- ");
}
super.printStackTrace(out);
if (cause != null) {
@ -120,7 +113,7 @@ public class XMPPException extends Exception {
public void printStackTrace(PrintWriter out) {
if (error != null) {
System.err.println(error);
System.err.print(error + " -- ");
}
super.printStackTrace(out);
if (cause != null) {

View File

@ -0,0 +1,163 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2003 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smack.packet;
import java.util.Map;
import java.util.Iterator;
/**
* Represents registration packets. An empty GET query will cause the server to return information
* about it's registration support. SET queries can be used to create accounts or update
* existing account information. XMPP servers may require a number of attributes to be set
* when creating a new account. The standard account attributes are as follows:
* <ul>
* <li>name -- the user's name.
* <li>first -- the user's first name.
* <li>last -- the user's last name.
* <li>email -- the user's email address.
* <li>city -- the user's city.
* <li>state -- the user's state.
* <li>zip -- the user's ZIP code.
* <li>phone -- the user's phone number.
* <li>url -- the user's website.
* <li>date -- the date the registration took place.
* <li>misc -- other miscellaneous information to associate with the account.
* <li>text -- textual information to associate with the account.
* <li>remove -- empty flag to remove account.
* </ul>
*
* @author Matt Tucker
*/
public class Registration extends IQ {
private String username = null;
private String password = null;
private Map attributes = null;
/**
* Returns the username, or <tt>null</tt> if no username has ben set.
*
* @return the username.
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username the username.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Returns the password, or <tt>null</tt> if no password has been set.
*
* @return the password.
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password the password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns the map of String key/value pairs of account attributes.
*
* @return the account attributes.
*/
public Map getAttributes() {
return attributes;
}
/**
* Sets the account attributes. The map must only contain String key/value pairs.
*
* @param attributes the account attributes.
*/
public void setAttributes(Map attributes) {
this.attributes = attributes;
}
public String getQueryXML() {
StringBuffer buf = new StringBuffer();
buf.append("<query xmlns=\"jabber:iq:register\">");
if (username != null) {
buf.append("<username>").append(username).append("</username>");
}
if (password != null) {
buf.append("<password>").append(password).append("</password>");
}
if (attributes != null && attributes.size() > 0) {
Iterator fieldNames = attributes.keySet().iterator();
while (fieldNames.hasNext()) {
String name = (String)fieldNames.next();
String value = (String)attributes.get(name);
buf.append("<").append(name).append(">");
buf.append(value);
buf.append("</").append(name).append(">");
}
}
buf.append("</query>");
return buf.toString();
}
}

View File

@ -141,9 +141,9 @@ public class XMPPError {
public String toString() {
StringBuffer txt = new StringBuffer();
txt.append(code);
txt.append("(").append(code).append(")");
if (message != null) {
txt.append(": ").append(message);
txt.append(" ").append(message);
}
return txt.toString();
}