Unverified Commit d16c186d authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #1049 from guusdk/OF-1511_Pubsub-publishing-options

OF-1511 OF-1513 OF-1514: Pubsub / PEP changes
parents 2e62750f b99be2d6
...@@ -207,7 +207,6 @@ public class IQOwnerHandler { ...@@ -207,7 +207,6 @@ public class IQOwnerHandler {
throws ForbiddenException, ConflictException, NotAcceptableException throws ForbiddenException, ConflictException, NotAcceptableException
{ {
List<String> values; List<String> values;
String booleanValue;
FormField field; FormField field;
// Get the new list of admins // Get the new list of admins
...@@ -262,9 +261,7 @@ public class IQOwnerHandler { ...@@ -262,9 +261,7 @@ public class IQOwnerHandler {
field = completedForm.getField("muc#roomconfig_changesubject"); field = completedForm.getField("muc#roomconfig_changesubject");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setCanOccupantsChangeSubject( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setCanOccupantsChangeSubject(("1".equals(booleanValue)));
} }
field = completedForm.getField("muc#roomconfig_maxusers"); field = completedForm.getField("muc#roomconfig_maxusers");
...@@ -281,16 +278,12 @@ public class IQOwnerHandler { ...@@ -281,16 +278,12 @@ public class IQOwnerHandler {
field = completedForm.getField("muc#roomconfig_publicroom"); field = completedForm.getField("muc#roomconfig_publicroom");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setPublicRoom( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setPublicRoom(("1".equals(booleanValue)));
} }
field = completedForm.getField("muc#roomconfig_persistentroom"); field = completedForm.getField("muc#roomconfig_persistentroom");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); boolean isPersistent = parseFirstValueAsBoolean( field, true );
booleanValue = ((value != null ? value : "1"));
boolean isPersistent = ("1".equals(booleanValue));
// Delete the room from the DB if it's no longer persistent // Delete the room from the DB if it's no longer persistent
if (room.isPersistent() && !isPersistent) { if (room.isPersistent() && !isPersistent) {
MUCPersistenceManager.deleteFromDB(room); MUCPersistenceManager.deleteFromDB(room);
...@@ -300,23 +293,17 @@ public class IQOwnerHandler { ...@@ -300,23 +293,17 @@ public class IQOwnerHandler {
field = completedForm.getField("muc#roomconfig_moderatedroom"); field = completedForm.getField("muc#roomconfig_moderatedroom");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setModerated( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setModerated(("1".equals(booleanValue)));
} }
field = completedForm.getField("muc#roomconfig_membersonly"); field = completedForm.getField("muc#roomconfig_membersonly");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); presences.addAll(room.setMembersOnly( parseFirstValueAsBoolean( field, true ) ) );
booleanValue = ((value != null ? value : "1"));
presences.addAll(room.setMembersOnly(("1".equals(booleanValue))));
} }
field = completedForm.getField("muc#roomconfig_allowinvites"); field = completedForm.getField("muc#roomconfig_allowinvites");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setCanOccupantsInvite( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setCanOccupantsInvite(("1".equals(booleanValue)));
} }
...@@ -330,9 +317,7 @@ public class IQOwnerHandler { ...@@ -330,9 +317,7 @@ public class IQOwnerHandler {
if (field != null) if (field != null)
{ {
passwordProtectionChanged = true; passwordProtectionChanged = true;
final String value = field.getFirstValue(); updatedIsPasswordProtected = parseFirstValueAsBoolean( field, true );
booleanValue = ( ( value != null ? value : "1" ) );
updatedIsPasswordProtected = "1".equals( booleanValue );
} }
field = completedForm.getField("muc#roomconfig_roomsecret"); field = completedForm.getField("muc#roomconfig_roomsecret");
...@@ -373,43 +358,32 @@ public class IQOwnerHandler { ...@@ -373,43 +358,32 @@ public class IQOwnerHandler {
field = completedForm.getField("muc#roomconfig_whois"); field = completedForm.getField("muc#roomconfig_whois");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setCanAnyoneDiscoverJID(("anyone".equals(field.getFirstValue())));
booleanValue = ((value != null ? value : "1"));
room.setCanAnyoneDiscoverJID(("anyone".equals(booleanValue)));
} }
field = completedForm.getField("muc#roomconfig_allowpm"); field = completedForm.getField("muc#roomconfig_allowpm");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setCanSendPrivateMessage(field.getFirstValue());
room.setCanSendPrivateMessage(value);
} }
field = completedForm.getField("muc#roomconfig_enablelogging"); field = completedForm.getField("muc#roomconfig_enablelogging");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setLogEnabled( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setLogEnabled(("1".equals(booleanValue)));
} }
field = completedForm.getField("x-muc#roomconfig_reservednick"); field = completedForm.getField("x-muc#roomconfig_reservednick");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setLoginRestrictedToNickname( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setLoginRestrictedToNickname(("1".equals(booleanValue)));
} }
field = completedForm.getField("x-muc#roomconfig_canchangenick"); field = completedForm.getField("x-muc#roomconfig_canchangenick");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setChangeNickname( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setChangeNickname(("1".equals(booleanValue)));
} }
field = completedForm.getField("x-muc#roomconfig_registration"); field = completedForm.getField("x-muc#roomconfig_registration");
if (field != null) { if (field != null) {
final String value = field.getFirstValue(); room.setRegistrationEnabled( parseFirstValueAsBoolean( field, true ) );
booleanValue = ((value != null ? value : "1"));
room.setRegistrationEnabled(("1".equals(booleanValue)));
} }
// Update the modification date to reflect the last time when the room's configuration // Update the modification date to reflect the last time when the room's configuration
...@@ -719,4 +693,33 @@ public class IQOwnerHandler { ...@@ -719,4 +693,33 @@ public class IQOwnerHandler {
probeResult = element; probeResult = element;
probeResult.add(configurationForm.getElement()); probeResult.add(configurationForm.getElement());
} }
/**
* Returns the first value of the formfield as a boolean.
*
* @param field A form field (cannot be null)
* @param defaultValue Returned if first value is null or empty.
* @return true if the provided input equals '1' or 'true', false if the input equals '0' or 'false'.
* @throws IllegalArgumentException when the input cannot be parsed as a boolean.
* @Deprecated Use {@link FormField#parseFirstValueAsBoolean(String)} provided by Tinder version 1.3.1 or newer.
*/
@Deprecated
public static boolean parseFirstValueAsBoolean( FormField field, boolean defaultValue )
{
final String value = field.getFirstValue();
if ( value == null || value.isEmpty() )
{
return defaultValue;
}
if ( "0".equals( value ) || "false".equals( value ) )
{
return false;
}
if ( "1".equals( value ) || "true".equals( value ) )
{
return true;
}
throw new IllegalArgumentException( "Unable to parse value '" + value + "' as Data Form Field boolean." );
}
} }
...@@ -369,6 +369,18 @@ public class IQPEPHandler extends IQHandler implements ServerIdentitiesProvider, ...@@ -369,6 +369,18 @@ public class IQPEPHandler extends IQHandler implements ServerIdentitiesProvider,
// Create the node // Create the node
final JID creator = new JID(jidFrom); final JID creator = new JID(jidFrom);
final LeafNode newNode = new LeafNode(pepService, pepService.getRootCollectionNode(), nodeID, creator); final LeafNode newNode = new LeafNode(pepService, pepService.getRootCollectionNode(), nodeID, creator);
final DataForm publishOptions = PubSubEngine.getPublishOptions( packet );
if ( publishOptions != null )
{
try
{
newNode.configure( publishOptions );
}
catch ( NotAcceptableException e )
{
Log.warn( "Unable to apply publish-options when creating a new PEP node {} for {}", nodeID, creator, e );
}
}
newNode.addOwner(creator); newNode.addOwner(creator);
newNode.saveToDB(); newNode.saveToDB();
} }
......
...@@ -194,7 +194,7 @@ public class CollectionNode extends Node { ...@@ -194,7 +194,7 @@ public class CollectionNode extends Node {
formField.addValue(maxLeafNodes); formField.addValue(maxLeafNodes);
formField = form.addField(); formField = form.addField();
formField.setVariable("pubsub#chilren_max"); formField.setVariable("pubsub#children_max");
if (isEditing) { if (isEditing) {
formField.setType(FormField.Type.text_single); formField.setType(FormField.Type.text_single);
formField.setLabel(LocaleUtils.getLocalizedString("pubsub.form.conf.children_max")); formField.setLabel(LocaleUtils.getLocalizedString("pubsub.form.conf.children_max"));
......
...@@ -33,6 +33,8 @@ import org.xmpp.packet.IQ; ...@@ -33,6 +33,8 @@ import org.xmpp.packet.IQ;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import static org.jivesoftware.openfire.muc.spi.IQOwnerHandler.parseFirstValueAsBoolean;
/** /**
* A type of node that contains published items only. It is NOT a container for * A type of node that contains published items only. It is NOT a container for
* other nodes. * other nodes.
...@@ -84,27 +86,19 @@ public class LeafNode extends Node { ...@@ -84,27 +86,19 @@ public class LeafNode extends Node {
@Override @Override
protected void configure(FormField field) throws NotAcceptableException { protected void configure(FormField field) throws NotAcceptableException {
List<String> values;
String booleanValue;
if ("pubsub#persist_items".equals(field.getVariable())) { if ("pubsub#persist_items".equals(field.getVariable())) {
values = field.getValues(); persistPublishedItems = parseFirstValueAsBoolean( field, true );
booleanValue = (values.size() > 0 ? values.get(0) : "1");
persistPublishedItems = "1".equals(booleanValue);
} }
else if ("pubsub#max_payload_size".equals(field.getVariable())) { else if ("pubsub#max_payload_size".equals(field.getVariable())) {
values = field.getValues(); maxPayloadSize = field.getFirstValue() != null ? Integer.parseInt( field.getFirstValue() ) : 5120;
maxPayloadSize = values.size() > 0 ? Integer.parseInt(values.get(0)) : 5120;
} }
else if ("pubsub#send_item_subscribe".equals(field.getVariable())) { else if ("pubsub#send_item_subscribe".equals(field.getVariable())) {
values = field.getValues(); sendItemSubscribe = parseFirstValueAsBoolean( field, true );
booleanValue = (values.size() > 0 ? values.get(0) : "1");
sendItemSubscribe = "1".equals(booleanValue);
} }
} }
@Override @Override
void postConfigure(DataForm completedForm) { void postConfigure(DataForm completedForm) {
List<String> values;
if (!persistPublishedItems) { if (!persistPublishedItems) {
// Always save the last published item when not configured to use persistent items // Always save the last published item when not configured to use persistent items
maxPublishedItems = 1; maxPublishedItems = 1;
...@@ -112,8 +106,7 @@ public class LeafNode extends Node { ...@@ -112,8 +106,7 @@ public class LeafNode extends Node {
else { else {
FormField field = completedForm.getField("pubsub#max_items"); FormField field = completedForm.getField("pubsub#max_items");
if (field != null) { if (field != null) {
values = field.getValues(); maxPublishedItems = field.getFirstValue() != null ? Integer.parseInt( field.getFirstValue() ) : 50;
maxPublishedItems = values.size() > 0 ? Integer.parseInt(values.get(0)) : 50;
} }
} }
} }
......
...@@ -44,6 +44,8 @@ import org.xmpp.packet.IQ; ...@@ -44,6 +44,8 @@ import org.xmpp.packet.IQ;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import static org.jivesoftware.openfire.muc.spi.IQOwnerHandler.parseFirstValueAsBoolean;
/** /**
* A virtual location to which information can be published and from which event * A virtual location to which information can be published and from which event
* notifications and/or payloads can be received (in other pubsub systems, this may * notifications and/or payloads can be received (in other pubsub systems, this may
...@@ -498,7 +500,6 @@ public abstract class Node { ...@@ -498,7 +500,6 @@ public abstract class Node {
} }
else if (DataForm.Type.submit.equals(completedForm.getType())) { else if (DataForm.Type.submit.equals(completedForm.getType())) {
List<String> values; List<String> values;
String booleanValue;
// Get the new list of owners // Get the new list of owners
FormField ownerField = completedForm.getField("pubsub#owner"); FormField ownerField = completedForm.getField("pubsub#owner");
...@@ -525,40 +526,26 @@ public abstract class Node { ...@@ -525,40 +526,26 @@ public abstract class Node {
// Do nothing // Do nothing
} }
else if ("pubsub#deliver_payloads".equals(field.getVariable())) { else if ("pubsub#deliver_payloads".equals(field.getVariable())) {
values = field.getValues(); deliverPayloads = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
deliverPayloads = "1".equals(booleanValue);
} }
else if ("pubsub#notify_config".equals(field.getVariable())) { else if ("pubsub#notify_config".equals(field.getVariable())) {
values = field.getValues(); notifyConfigChanges = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
notifyConfigChanges = "1".equals(booleanValue);
} }
else if ("pubsub#notify_delete".equals(field.getVariable())) { else if ("pubsub#notify_delete".equals(field.getVariable())) {
values = field.getValues(); notifyDelete = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
notifyDelete = "1".equals(booleanValue);
} }
else if ("pubsub#notify_retract".equals(field.getVariable())) { else if ("pubsub#notify_retract".equals(field.getVariable())) {
values = field.getValues(); notifyRetract = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
notifyRetract = "1".equals(booleanValue);
} }
else if ("pubsub#presence_based_delivery".equals(field.getVariable())) { else if ("pubsub#presence_based_delivery".equals(field.getVariable())) {
values = field.getValues(); presenceBasedDelivery = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
presenceBasedDelivery = "1".equals(booleanValue);
} }
else if ("pubsub#subscribe".equals(field.getVariable())) { else if ("pubsub#subscribe".equals(field.getVariable())) {
values = field.getValues(); subscriptionEnabled = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
subscriptionEnabled = "1".equals(booleanValue);
} }
else if ("pubsub#subscription_required".equals(field.getVariable())) { else if ("pubsub#subscription_required".equals(field.getVariable())) {
// TODO Replace this variable for the one defined in the JEP (once one is defined) // TODO Replace this variable for the one defined in the JEP (once one is defined)
values = field.getValues(); subscriptionConfigurationRequired = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
subscriptionConfigurationRequired = "1".equals(booleanValue);
} }
else if ("pubsub#type".equals(field.getVariable())) { else if ("pubsub#type".equals(field.getVariable())) {
values = field.getValues(); values = field.getValues();
......
...@@ -36,6 +36,8 @@ import org.xmpp.packet.JID; ...@@ -36,6 +36,8 @@ import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import org.xmpp.packet.Presence; import org.xmpp.packet.Presence;
import static org.jivesoftware.openfire.muc.spi.IQOwnerHandler.parseFirstValueAsBoolean;
/** /**
* A subscription to a node. Entities may subscribe to a node to be notified when new events * A subscription to a node. Entities may subscribe to a node to be notified when new events
* are published to the node. Published events may contain a {@link PublishedItem}. Only * are published to the node. Published events may contain a {@link PublishedItem}. Only
...@@ -409,7 +411,6 @@ public class NodeSubscription { ...@@ -409,7 +411,6 @@ public class NodeSubscription {
void configure(DataForm options) { void configure(DataForm options) {
List<String> values; List<String> values;
String booleanValue;
boolean wasUsingPresence = !presenceStates.isEmpty(); boolean wasUsingPresence = !presenceStates.isEmpty();
...@@ -435,14 +436,10 @@ public class NodeSubscription { ...@@ -435,14 +436,10 @@ public class NodeSubscription {
for (FormField field : options.getFields()) { for (FormField field : options.getFields()) {
boolean fieldExists = true; boolean fieldExists = true;
if ("pubsub#deliver".equals(field.getVariable())) { if ("pubsub#deliver".equals(field.getVariable())) {
values = field.getValues(); deliverNotifications = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
deliverNotifications = "1".equals(booleanValue);
} }
else if ("pubsub#digest".equals(field.getVariable())) { else if ("pubsub#digest".equals(field.getVariable())) {
values = field.getValues(); usingDigest = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
usingDigest = "1".equals(booleanValue);
} }
else if ("pubsub#digest_frequency".equals(field.getVariable())) { else if ("pubsub#digest_frequency".equals(field.getVariable())) {
values = field.getValues(); values = field.getValues();
...@@ -457,9 +454,7 @@ public class NodeSubscription { ...@@ -457,9 +454,7 @@ public class NodeSubscription {
} }
} }
else if ("pubsub#include_body".equals(field.getVariable())) { else if ("pubsub#include_body".equals(field.getVariable())) {
values = field.getValues(); includingBody = parseFirstValueAsBoolean( field, true ) ;
booleanValue = (values.size() > 0 ? values.get(0) : "1");
includingBody = "1".equals(booleanValue);
} }
else if ("pubsub#show-values".equals(field.getVariable())) { else if ("pubsub#show-values".equals(field.getVariable())) {
// Get the new list of presence states for which an entity wants to // Get the new list of presence states for which an entity wants to
......
...@@ -19,11 +19,7 @@ package org.jivesoftware.openfire.pubsub; ...@@ -19,11 +19,7 @@ package org.jivesoftware.openfire.pubsub;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.QName; import org.dom4j.QName;
import org.jivesoftware.openfire.PacketRouter; import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.RoutingTable;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.XMPPServerListener;
import org.jivesoftware.openfire.component.InternalComponentManager; import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.pep.PEPService; import org.jivesoftware.openfire.pep.PEPService;
import org.jivesoftware.openfire.pubsub.cluster.RefreshNodeTask; import org.jivesoftware.openfire.pubsub.cluster.RefreshNodeTask;
...@@ -37,20 +33,9 @@ import org.slf4j.Logger; ...@@ -37,20 +33,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xmpp.forms.DataForm; import org.xmpp.forms.DataForm;
import org.xmpp.forms.FormField; import org.xmpp.forms.FormField;
import org.xmpp.packet.IQ; import org.xmpp.packet.*;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import java.util.*;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future; import java.util.concurrent.Future;
...@@ -133,7 +118,7 @@ public class PubSubEngine { ...@@ -133,7 +118,7 @@ public class PubSubEngine {
return TaskEngine.getInstance().submit(new Runnable() { return TaskEngine.getInstance().submit(new Runnable() {
@Override @Override
public void run() { public void run() {
createNode(service, iq, childElement, finalAction); createNode(service, iq, childElement, finalAction, getPublishOptions( iq ));
} }
}); });
} }
...@@ -338,34 +323,42 @@ public class PubSubEngine { ...@@ -338,34 +323,42 @@ public class PubSubEngine {
if (nodeID == null) { if (nodeID == null) {
// XEP-0060 Section 7.2.3.3 - No node was specified. Return bad_request error // XEP-0060 Section 7.2.3.3 - No node was specified. Return bad_request error
// This suggests that Instant nodes should not be auto-created // This suggests that Instant nodes should not be auto-created
Element pubsubError = DocumentHelper.createElement(QName.get( Element pubsubError = DocumentHelper.createElement(QName.get("nodeid-required", "http://jabber.org/protocol/pubsub#errors"));
"nodeid-required", "http://jabber.org/protocol/pubsub#errors"));
sendErrorPacket(iq, PacketError.Condition.bad_request, pubsubError); sendErrorPacket(iq, PacketError.Condition.bad_request, pubsubError);
return; return;
} }
else {
// Look for the specified node // Optional Publish Options.
node = service.getNode(nodeID); final DataForm publishOptions = getPublishOptions( iq );
if (node == null) {
if (service instanceof PEPService && service.isServiceAdmin(owner)){ // Look for the specified node
// If it is a PEP service & publisher is service owner - node = service.getNode(nodeID);
// auto create nodes. if (node == null) {
Element childElement = iq.getChildElement(); if (service instanceof PEPService && service.isServiceAdmin(owner) && canAutoCreate( publishOptions ) ) {
CreateNodeResponse response = createNodeHelper(service, iq, childElement, publishElement); // If it is a PEP service & publisher is service owner - auto create nodes.
CreateNodeResponse response = createNodeHelper(service, iq, iq.getChildElement(), publishElement, publishOptions);
if (response.newNode == null) {
// New node creation failed. Since pep#auto-create is advertised if (response.newNode == null) {
// in disco#info, node creation error should be sent to the client. // New node creation failed. Since pep#auto-create is advertised
sendErrorPacket(iq, response.creationStatus, response.pubsubError); // in disco#info, node creation error should be sent to the client.
} else { sendErrorPacket(iq, response.creationStatus, response.pubsubError);
// Node creation succeeded, set node to newNode.
node = response.newNode;
}
} else {
// Node does not exist. Return item-not-found error
sendErrorPacket(iq, PacketError.Condition.item_not_found, null);
return; return;
} else {
// Node creation succeeded, set node to newNode.
node = response.newNode;
} }
} else {
// Node does not exist. Return item-not-found error
sendErrorPacket(iq, PacketError.Condition.item_not_found, null);
return;
}
} else {
// Check if the preconditions defined in the publish options (if any) are met.
if ( !nodeMeetsPreconditions( node, publishOptions ) )
{
Element pubsubError = DocumentHelper.createElement(QName.get("precondition-not-met", "http://jabber.org/protocol/pubsub#errors"));
sendErrorPacket(iq, PacketError.Condition.conflict, pubsubError);
return;
} }
} }
...@@ -433,6 +426,120 @@ public class PubSubEngine { ...@@ -433,6 +426,120 @@ public class PubSubEngine {
leafNode.publishItems(from, items); leafNode.publishItems(from, items);
} }
/**
* Get the dataform that describes the publish options from the request, or null if no such form was included.
*
* @param iq The publish request (cannot be null).
* @return A publish options data form (possibly null).
*/
public static DataForm getPublishOptions( IQ iq )
{
final Element publishOptionsElement = iq.getChildElement().element( "publish-options" );
if ( publishOptionsElement == null )
{
return null;
}
final Element x = publishOptionsElement.element( QName.get( DataForm.ELEMENT_NAME, DataForm.NAMESPACE ) );
if ( x == null )
{
return null;
}
final DataForm result = new DataForm( x );
if ( result.getType() != DataForm.Type.submit )
{
return null;
}
final FormField formType = result.getField( "FORM_TYPE" );
if ( formType == null || !"http://jabber.org/protocol/pubsub#publish-options".equals( formType.getFirstValue() ) )
{
return null;
}
return result;
}
/**
* Checks if a node is allowed to be auto-created, given the configuration of the service and the optional publish options.
*
* @param publishOptions publish options (can be null)
* @return true if auto-creation of nodes on publish to a non-existent node is allowed, otherwise false.
*/
private boolean canAutoCreate( DataForm publishOptions )
{
// Since pep#auto-create is advertised in disco#info in hard-code, this is always allowed, unless the publish options explicitly forbid this.
if ( publishOptions == null )
{
return true;
}
final FormField field = publishOptions.getField( "pubsub#auto-create" );
if ( field == null )
{
return true;
}
final String firstValue = field.getFirstValue();
return "1".equals( firstValue ) || "true".equalsIgnoreCase( firstValue );
}
/**
* Checks of the configuration of the node meets the preconditions, as supplied in the dataform.
*
* This method returns true only if the configuration of the node at least contains each of the fields
* defined in the preconditions (with matching values).
*
* @param node The node (cannot be null)
* @param preconditions The preconditions (can be null, in which case 'true' is returned).
* @return True if all preconditions are met, otherwise false.
*/
private boolean nodeMeetsPreconditions( Node node, DataForm preconditions )
{
if ( preconditions == null )
{
return true;
}
final DataForm conditions = node.getConfigurationForm();
for ( final FormField precondition : preconditions.getFields() )
{
if ( precondition.getVariable().equals( "FORM_TYPE" ) )
{
continue;
}
final FormField condition = conditions.getField( precondition.getVariable() );
if ( condition == null )
{
// Unknown condition. Reject.
return false;
}
if ( condition.getValues().size() > 1 )
{
if ( !condition.getValues().containsAll( precondition.getValues() ) || !precondition.getValues().containsAll( condition.getValues() ) )
{
// The condition value list does contain different values than the precondtion value list.
return false;
}
}
else
{
final String a = condition.getFirstValue();
final String b = precondition.getFirstValue();
if ( !a.equals( b ) && !(a.equals( "true" ) && b.equals( "1" )) && !(a.equals( "1" ) && b.equals( "true" ))
&& !(a.equals( "false" ) && b.equals( "0" )) && !(a.equals( "0" ) && b.equals( "false" )) )
{
return false;
}
}
}
return true;
}
private void deleteItems(PubSubService service, IQ iq, Element retractElement) { private void deleteItems(PubSubService service, IQ iq, Element retractElement) {
String nodeID = retractElement.attributeValue("node"); String nodeID = retractElement.attributeValue("node");
Node node; Node node;
...@@ -1129,9 +1236,9 @@ public class PubSubEngine { ...@@ -1129,9 +1236,9 @@ public class PubSubEngine {
leafNode.sendPublishedItems(iq, items, forceToIncludePayload); leafNode.sendPublishedItems(iq, items, forceToIncludePayload);
} }
private void createNode(PubSubService service, IQ iq, Element childElement, Element createElement) { private void createNode(PubSubService service, IQ iq, Element childElement, Element createElement, DataForm publishOptions) {
// Call createNodeHelper and get the node creation status. // Call createNodeHelper and get the node creation status.
CreateNodeResponse response = createNodeHelper(service, iq, childElement, createElement); CreateNodeResponse response = createNodeHelper(service, iq, childElement, createElement, publishOptions);
if (response.newNode == null) { if (response.newNode == null) {
// New node creation failed // New node creation failed
sendErrorPacket(iq, response.creationStatus, response.pubsubError); sendErrorPacket(iq, response.creationStatus, response.pubsubError);
...@@ -1174,13 +1281,9 @@ public class PubSubEngine { ...@@ -1174,13 +1281,9 @@ public class PubSubEngine {
* <br/>NOTE 2: This method calls UserManager::isRegisteredUser(JID) which can block waiting for a response - so * <br/>NOTE 2: This method calls UserManager::isRegisteredUser(JID) which can block waiting for a response - so
* do not call this method in the same thread in which a response might arrive * do not call this method in the same thread in which a response might arrive
* *
* @param service
* @param iq
* @param childElement
* @param createElement
* @return {@link CreateNodeResponse} * @return {@link CreateNodeResponse}
*/ */
private CreateNodeResponse createNodeHelper(PubSubService service, IQ iq, Element childElement, Element createElement) { private CreateNodeResponse createNodeHelper(PubSubService service, IQ iq, Element childElement, Element createElement, DataForm publishOptions) {
// Get sender of the IQ packet // Get sender of the IQ packet
JID from = iq.getFrom(); JID from = iq.getFrom();
// Verify that sender has permissions to create nodes // Verify that sender has permissions to create nodes
...@@ -1213,36 +1316,53 @@ public class PubSubEngine { ...@@ -1213,36 +1316,53 @@ public class PubSubEngine {
Element configureElement = childElement.element("configure"); Element configureElement = childElement.element("configure");
if (configureElement != null) { if (configureElement != null) {
// Get the data form that contains the parent nodeID // Get the data form that contains the parent nodeID
completedForm = getSentConfigurationForm(configureElement); completedForm = getSentConfigurationForm( configureElement );
if (completedForm != null) { }
// Calculate newNodeID when new node is affiliated with a Collection
FormField field = completedForm.getField("pubsub#collection"); if (publishOptions != null) {
if (field != null) { // Apply publish options to override provided config.
List<String> values = field.getValues(); if ( completedForm == null ) {
if (!values.isEmpty()) { completedForm = publishOptions;
String parentNodeID = values.get(0); } else {
Node tempNode = service.getNode(parentNodeID); for ( final FormField publishOption : publishOptions.getFields() ) {
if (tempNode == null) { completedForm.removeField( publishOption.getVariable() );
// Requested parent node was not found so return an error final FormField formField = completedForm.addField( publishOption.getVariable(), publishOption.getLabel(), publishOption.getType() );
return new CreateNodeResponse(PacketError.Condition.item_not_found, null, null); for ( final String value : publishOption.getValues() ) {
} formField.addValue( value );
else if (!tempNode.isCollectionNode()) {
// Requested parent node is not a collection node so return an error
return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
}
parentNode = (CollectionNode) tempNode;
} }
} }
field = completedForm.getField("pubsub#node_type"); }
if (field != null) { }
// Check if user requested to create a new collection node
List<String> values = field.getValues(); if (completedForm != null) {
if (!values.isEmpty()) { // Calculate newNodeID when new node is affiliated with a Collection
collectionType = "collection".equals(values.get(0)); FormField field = completedForm.getField("pubsub#collection");
if (field != null) {
List<String> values = field.getValues();
if (!values.isEmpty()) {
String parentNodeID = values.get(0);
Node tempNode = service.getNode(parentNodeID);
if (tempNode == null) {
// Requested parent node was not found so return an error
return new CreateNodeResponse(PacketError.Condition.item_not_found, null, null);
}
else if (!tempNode.isCollectionNode()) {
// Requested parent node is not a collection node so return an error
return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
} }
parentNode = (CollectionNode) tempNode;
}
}
field = completedForm.getField("pubsub#node_type");
if (field != null) {
// Check if user requested to create a new collection node
List<String> values = field.getValues();
if (!values.isEmpty()) {
collectionType = "collection".equals(values.get(0));
} }
} }
} }
// If no parent was defined then use the root collection node // If no parent was defined then use the root collection node
if (parentNode == null && service.isCollectionNodesSupported()) { if (parentNode == null && service.isCollectionNodesSupported()) {
parentNode = service.getRootCollectionNode(); parentNode = service.getRootCollectionNode();
......
...@@ -679,6 +679,8 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di ...@@ -679,6 +679,8 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di
features.add("http://jabber.org/protocol/pubsub#subscribe"); features.add("http://jabber.org/protocol/pubsub#subscribe");
// Configuration of subscription options is supported // Configuration of subscription options is supported
features.add("http://jabber.org/protocol/pubsub#subscription-options"); features.add("http://jabber.org/protocol/pubsub#subscription-options");
// Publishing options are supported.
features.add("http://jabber.org/protocol/pubsub#publish-options");
} }
else if (name == null) { else if (name == null) {
// Answer the features of a given node // Answer the features of a given node
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment