1 /*
2  * Copyright (C) 2004-2008 Jive Software. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.jivesoftware.openfire.muc.spi;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.locks.Lock;
22 
23 import org.dom4j.DocumentHelper;
24 import org.dom4j.Element;
25 import org.dom4j.QName;
26 import org.jivesoftware.openfire.muc.ConflictException;
27 import org.jivesoftware.openfire.muc.ForbiddenException;
28 import org.jivesoftware.openfire.muc.MUCRole;
29 import org.jivesoftware.openfire.muc.MUCRoom;
30 import org.jivesoftware.openfire.muc.MultiUserChatService;
31 import org.jivesoftware.util.ElementUtil;
32 import org.jivesoftware.util.LocaleUtils;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.xmpp.forms.DataForm;
36 import org.xmpp.forms.FormField;
37 import org.xmpp.packet.IQ;
38 import org.xmpp.packet.PacketError;
39 import org.xmpp.packet.Presence;
40 
41 /**
42  * This class is responsible for handling packets with namespace jabber:iq:register that were
43  * sent to the MUC service.  MultiUserChatServer will receive all the IQ packets and if the
44  * namespace of the IQ is jabber:iq:register then this class will handle the packet.
45  *
46  * @author Gaston Dombiak
47  */
48 class IQMUCRegisterHandler {
49 
50     private static final Logger Log = LoggerFactory.getLogger(IQMUCRegisterHandler.class);
51 
52     private static final Element probeResult;
53 
54     static {
55         // Create the registration form of the room which contains information
56         // such as: first name, last name and  nickname.
57         final DataForm registrationForm = new DataForm(DataForm.Type.form);
58         registrationForm.setTitle(LocaleUtils.getLocalizedString("muc.form.reg.title"));
registrationForm.addInstruction(LocaleUtils .getLocalizedString(R))59         registrationForm.addInstruction(LocaleUtils
60                 .getLocalizedString("muc.form.reg.instruction"));
61 
62         final FormField fieldForm = registrationForm.addField();
63         fieldForm.setVariable("FORM_TYPE");
64         fieldForm.setType(FormField.Type.hidden);
65         fieldForm.addValue("http://jabber.org/protocol/muc#register");
66 
67         final FormField fieldReg = registrationForm.addField();
68         fieldReg.setVariable("muc#register_first");
69         fieldReg.setType(FormField.Type.text_single);
70         fieldReg.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.first-name"));
71         fieldReg.setRequired(true);
72 
73         final FormField fieldLast = registrationForm.addField();
74         fieldLast.setVariable("muc#register_last");
75         fieldLast.setType(FormField.Type.text_single);
76         fieldLast.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.last-name"));
77         fieldLast.setRequired(true);
78 
79         final FormField fieldNick = registrationForm.addField();
80         fieldNick.setVariable("muc#register_roomnick");
81         fieldNick.setType(FormField.Type.text_single);
82         fieldNick.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.nickname"));
83         fieldNick.setRequired(true);
84 
85         final FormField fieldUrl = registrationForm.addField();
86         fieldUrl.setVariable("muc#register_url");
87         fieldUrl.setType(FormField.Type.text_single);
88         fieldUrl.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.url"));
89 
90         final FormField fieldMail = registrationForm.addField();
91         fieldMail.setVariable("muc#register_email");
92         fieldMail.setType(FormField.Type.text_single);
93         fieldMail.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.email"));
94 
95         final FormField fieldFaq = registrationForm.addField();
96         fieldFaq.setVariable("muc#register_faqentry");
97         fieldFaq.setType(FormField.Type.text_single);
98         fieldFaq.setLabel(LocaleUtils.getLocalizedString("muc.form.reg.faqentry"));
99 
100         // Create the probeResult and add the registration form
101         probeResult = DocumentHelper.createElement(QName.get("query", "jabber:iq:register"));
registrationForm.getElement()102         probeResult.add(registrationForm.getElement());
103     }
104 
105     private final MultiUserChatService mucService;
106 
IQMUCRegisterHandler(MultiUserChatService mucService)107     public IQMUCRegisterHandler(MultiUserChatService mucService) {
108         this.mucService = mucService;
109     }
110 
handleIQ(IQ packet)111     public IQ handleIQ(IQ packet) {
112         IQ reply = null;
113         // Get the target room
114         MUCRoom room = null;
115         String name = packet.getTo().getNode();
116         if (name == null) {
117             // Can't register with the service itself, so answer a feature-not-implemented error
118             reply = IQ.createResultIQ(packet);
119             reply.setChildElement(packet.getChildElement().createCopy());
120             reply.setError(PacketError.Condition.feature_not_implemented);
121             return reply;
122         }
123 
124         final Lock lock = mucService.getChatRoomLock(name);
125         lock.lock();
126         try {
127             room = mucService.getChatRoom(name);
128             if (room == null) {
129                 // The room doesn't exist so answer a NOT_FOUND error
130                 reply = IQ.createResultIQ(packet);
131                 reply.setChildElement(packet.getChildElement().createCopy());
132                 reply.setError(PacketError.Condition.item_not_found);
133                 return reply;
134             }
135             else if (!room.isRegistrationEnabled() ||
136                      (packet.getFrom() != null &&
137                       MUCRole.Affiliation.outcast == room.getAffiliation(packet.getFrom().asBareJID()))) {
138                 // The room does not accept users to register or
139                 // the user is an outcast and is not allowed to register
140                 reply = IQ.createResultIQ(packet);
141                 reply.setChildElement(packet.getChildElement().createCopy());
142                 reply.setError(PacketError.Condition.not_allowed);
143                 return reply;
144             }
145 
146             if (IQ.Type.get == packet.getType()) {
147                 reply = IQ.createResultIQ(packet);
148                 String nickname = room.getReservedNickname(packet.getFrom());
149                 Element currentRegistration = probeResult.createCopy();
150                 if (nickname != null) {
151                     // The user is already registered with the room so answer a completed form
152                     ElementUtil.setProperty(currentRegistration, "query.registered", null);
153                     currentRegistration.addElement("username").addText(nickname);
154 
155                     Element form = currentRegistration.element(QName.get("x", "jabber:x:data"));
156                     currentRegistration.remove(form);
157         //                @SuppressWarnings("unchecked")
158         //				Iterator<Element> fields = form.elementIterator("field");
159         //
160         //                Element field;
161         //                while (fields.hasNext()) {
162         //                    field = fields.next();
163         //                    if ("muc#register_roomnick".equals(field.attributeValue("var"))) {
164         //                        field.addElement("value").addText(nickname);
165         //                    }
166         //                }
167                     reply.setChildElement(currentRegistration);
168                 }
169                 else {
170                     // The user is not registered with the room so answer an empty form
171                     reply.setChildElement(currentRegistration);
172                 }
173             }
174             else if (IQ.Type.set ==  packet.getType()) {
175                 try {
176                     // Keep a registry of the updated presences
177                     List<Presence> presences = new ArrayList<>();
178 
179                     reply = IQ.createResultIQ(packet);
180                     Element iq = packet.getChildElement();
181 
182                     if (ElementUtil.includesProperty(iq, "query.remove")) {
183                         // The user is deleting his registration
184                         presences.addAll(room.addNone(packet.getFrom(), room.getRole()));
185                     }
186                     else {
187                         // The user is trying to register with a room
188                         Element formElement = iq.element("x");
189                         // Check if a form was used to provide the registration info
190                         if (formElement != null) {
191                             // Get the sent form
192                             final DataForm registrationForm = new DataForm(formElement);
193                             // Get the desired nickname sent in the form
194                             List<String> values = registrationForm.getField("muc#register_roomnick")
195                                     .getValues();
196                             String nickname = (!values.isEmpty() ? values.get(0) : null);
197 
198                             // TODO The rest of the fields of the form are ignored. If we have a
199                             // requirement in the future where we need those fields we'll have to change
200                             // MUCRoom.addMember in order to receive a RegistrationInfo (new class)
201 
202                             // Add the new member to the members list
203                             presences.addAll(room.addMember(packet.getFrom(),
204                                     nickname,
205                                     room.getRole()));
206                         }
207                         else {
208                             reply.setChildElement(packet.getChildElement().createCopy());
209                             reply.setError(PacketError.Condition.bad_request);
210                         }
211                     }
212                     // Send the updated presences to the room occupants
213                     for (Presence presence : presences) {
214                         room.send(presence, room.getRole());
215                     }
216 
217                 }
218                 catch (ForbiddenException e) {
219                     reply = IQ.createResultIQ(packet);
220                     reply.setChildElement(packet.getChildElement().createCopy());
221                     reply.setError(PacketError.Condition.forbidden);
222                 }
223                 catch (ConflictException e) {
224                     reply = IQ.createResultIQ(packet);
225                     reply.setChildElement(packet.getChildElement().createCopy());
226                     reply.setError(PacketError.Condition.conflict);
227                 }
228                 catch (Exception e) {
229                     Log.error(e.getMessage(), e);
230                 }
231             }
232 
233             // Ensure that other cluster nodes see the changes applied above.
234             mucService.syncChatRoom(room);
235         } finally {
236             lock.unlock();
237         }
238         return reply;
239     }
240 }
241