/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev$
* Last modified by $Author$
* $Date$
*/
package tigase.xmpp.impl;
//~--- non-JDK imports --------------------------------------------------------
import tigase.db.NonAuthUserRepository;
import tigase.db.TigaseDBException;
import tigase.server.Command;
import tigase.server.Packet;
import tigase.server.Priority;
import tigase.util.TigaseStringprepException;
import tigase.xml.Element;
import tigase.xmpp.Authorization;
import tigase.xmpp.BareJID;
import tigase.xmpp.NotAuthorizedException;
import tigase.xmpp.StanzaType;
import tigase.xmpp.XMPPException;
import tigase.xmpp.XMPPProcessor;
import tigase.xmpp.XMPPProcessorIfc;
import tigase.xmpp.XMPPResourceConnection;
//~--- JDK imports ------------------------------------------------------------
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
//~--- classes ----------------------------------------------------------------
/**
* JEP-0077: In-Band Registration
*
*
* Created: Thu Feb 16 13:14:06 2006
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev$
*/
public class JabberIqRegister extends XMPPProcessor implements XMPPProcessorIfc {
/**
* Private logger for class instances.
*/
private static Logger log = Logger.getLogger(JabberIqRegister.class.getName());
private static final String ID = "jabber:iq:register";
private static final String[] ELEMENTS = { "query" };
private static final String[] XMLNSS = { "jabber:iq:register" };
private static final Element[] FEATURES =
{ new Element("register", new String[] { "xmlns" },
new String[] { "http://jabber.org/features/iq-register" }) };
private static final Element[] DISCO_FEATURES = { new Element("feature",
new String[] { "var" }, new String[] { "jabber:iq:register" }) };
// ~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public String id() {
return ID;
}
/**
* Method description
*
*
* @param packet
* @param session
* @param repo
* @param results
* @param settings
*
* @throws XMPPException
* TODO: Implement registration form configurable and loading all
* the fields from the registration form TODO: rewrite the plugin
* using the XMPPProcessorAbstract API
*/
@Override
public void process(Packet packet, XMPPResourceConnection session,
NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)
throws XMPPException {
if (log.isLoggable(Level.FINEST)) {
log.finest("Processing packet: " + packet.toString());
}
if (session == null) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Session is null, ignoring");
}
return;
} // end of if (session == null)
BareJID id = session.getDomainAsJID().getBareJID();
if (packet.getStanzaTo() != null) {
id = packet.getStanzaTo().getBareJID();
}
try {
// I think it does not make sense to check the 'to', just the connection
// ID
// if ((id.equals(session.getDomain()) ||
// id.equals(session.getUserId().toString()))
// && packet.getFrom().equals(session.getConnectionId())) {
// Wrong thinking. The user may send an request from his own account
// to register with a transport or any other service, then the connection
// ID matches the session id but this is still not a request to the local
// server. The TO address must be checked too.....
// if (packet.getPacketFrom().equals(session.getConnectionId())) {
if ((packet.getPacketFrom() != null)
&& packet.getPacketFrom().equals(session.getConnectionId())
&& (!session.isAuthorized() || (session.isUserId(id) || session.isLocalDomain(
id.toString(), false)))) {
// We want to allow password change but not user registration if
// registration is disabled. The only way to tell apart registration
// from password change is to check whether the user is authenticated.
// For authenticated user the request means password change, otherwise
// registration attempt.
// Account deregistration is also called under authenticated session, so
// it should be blocked here if registration for domain is disabled.
// Assuming if user cannot register account he cannot also deregister account
Element request = packet.getElement();
boolean remove = request.findChild("/iq/query/remove") != null;
if ((!session.isAuthorized() || remove) && !session.getDomain().isRegisterEnabled()) {
results.offer(Authorization.NOT_ALLOWED.getResponseMessage(packet,
"Registration is not allowed for this domain.", true));
return;
}
Authorization result = Authorization.NOT_AUTHORIZED;
StanzaType type = packet.getType();
switch (type) {
case set:
// Is it registration cancel request?
Element elem = request.findChild("/iq/query/remove");
if (elem != null) {
// Yes this is registration cancel request
// According to JEP-0077 there must not be any
// more subelements apart from <remove/>
elem = request.findChild("/iq/query");
if (elem.getChildren().size() > 1) {
result = Authorization.BAD_REQUEST;
} else {
try {
result = session.unregister(packet.getStanzaFrom().toString());
Packet ok_result = packet.okResult((String) null, 0);
// We have to set SYSTEM priority for the packet here,
// otherwise the network connection is closed before the
// client received a response
ok_result.setPriority(Priority.SYSTEM);
results.offer(ok_result);
Packet close_cmd =
Command.CLOSE.getPacket(session.getSMComponentId(),
session.getConnectionId(), StanzaType.set,
session.nextStanzaId());
close_cmd.setPacketTo(session.getConnectionId());
results.offer(close_cmd);
} catch (NotAuthorizedException e) {
results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,
"You must authorize session first.", true));
} // end of try-catch
}
} else {
// No, so assuming this is registration of a new
// user or change registration details for existing user
String user_name = request.getChildCData("/iq/query/username");
String password = request.getChildCData("/iq/query/password");
String email = request.getChildCData("/iq/query/email");
Map<String, String> reg_params = null;
if ((email != null) && !email.trim().isEmpty()) {
reg_params = new LinkedHashMap<String, String>();
reg_params.put("email", email);
}
result = session.register(user_name, password, reg_params);
if (result == Authorization.AUTHORIZED) {
results.offer(result.getResponseMessage(packet, null, false));
} else {
results.offer(result.getResponseMessage(packet,
"Unsuccessful registration attempt", true));
}
}
break;
case get:
results.offer(packet.okResult("<instructions>"
+ "Choose a user name and password for use with this service."
+ "Please provide also your e-mail address." + "</instructions>"
+ "<username/>" + "<password/>" + "<email/>", 1));
break;
case result:
// It might be a registration request from transport for example...
Packet pack_res = packet.copyElementOnly();
pack_res.setPacketTo(session.getConnectionId());
results.offer(pack_res);
break;
default:
results.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,
"Message type is incorrect", true));
break;
} // end of switch (type)
} else {
if (session.isUserId(id)) {
// It might be a registration request from transport for example...
Packet pack_res = packet.copyElementOnly();
pack_res.setPacketTo(session.getConnectionId());
results.offer(pack_res);
} else {
results.offer(packet.copyElementOnly());
}
}
} catch (TigaseStringprepException ex) {
results.offer(Authorization.JID_MALFORMED.getResponseMessage(packet,
"Incorrect user name, stringprep processing failed.", true));
} catch (NotAuthorizedException e) {
results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,
"You are not authorized to change registration settings.\n" + e.getMessage(),
true));
} catch (TigaseDBException e) {
log.warning("Database problem: " + e);
results.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,
"Database access problem, please contact administrator.", true));
} // end of try-catch
}
/**
* Method description
*
*
* @param session
*
* @return
*/
@Override
public Element[] supDiscoFeatures(XMPPResourceConnection session) {
if (log.isLoggable(Level.FINEST) && session != null) {
log.finest("VHostItem: " + session.getDomain());
}
if (session != null && session.getDomain().isRegisterEnabled()) {
return DISCO_FEATURES;
} else {
return null;
}
}
/**
* Method description
*
*
* @return
*/
@Override
public String[] supElements() {
return ELEMENTS;
}
/**
* Method description
*
*
* @return
*/
@Override
public String[] supNamespaces() {
return XMLNSS;
}
/**
* Method description
*
*
* @param session
*
* @return
*/
@Override
public Element[] supStreamFeatures(XMPPResourceConnection session) {
if (log.isLoggable(Level.FINEST) && session != null) {
log.finest("VHostItem: " + session.getDomain());
}
if (session != null && session.getDomain().isRegisterEnabled()) {
return FEATURES;
} else {
return null;
}
}
} // JabberIqRegister
// ~ Formatted in Sun Code Convention
// ~ Formatted by Jindent --- http://www.jindent.com