/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.vysper.xmpp.modules.extension.xep0050_adhoc_commands;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.vysper.xml.fragment.XMLElement;
import org.apache.vysper.xml.fragment.XMLSemanticError;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
import org.apache.vysper.xmpp.server.ServerRuntimeContext;
import org.apache.vysper.xmpp.server.SessionContext;
import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
import org.apache.vysper.xmpp.stanza.IQStanza;
import org.apache.vysper.xmpp.stanza.IQStanzaType;
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.stanza.StanzaBuilder;
import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
import org.apache.vysper.xmpp.stanza.StanzaErrorType;
/**
*/
public class AdhocCommandIQHandler extends DefaultIQHandler {
protected final Collection<AdhocCommandSupport> adhocCommandSupporters;
protected final Map<String, AdhocCommandHandler> runningCommands = new HashMap<String, AdhocCommandHandler>();
public AdhocCommandIQHandler(Collection<AdhocCommandSupport> adhocCommandSupporters) {
this.adhocCommandSupporters = adhocCommandSupporters;
}
@Override
protected boolean verifyNamespace(Stanza stanza) {
return verifyInnerNamespace(stanza, NamespaceURIs.XEP0050_ADHOC_COMMANDS);
}
@Override
protected boolean verifyInnerElement(Stanza stanza) {
return verifyInnerElementWorker(stanza, "command");
}
@Override
protected Stanza handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
Entity from = stanza.getFrom();
if (from == null) {
from = sessionContext.getInitiatingEntity();
}
AdhocCommandHandler commandHandler = null;
String commandNode;
String requestedSessionId;
String action; // execute | cancel
List<XMLElement> commandElements = null;
try {
XMLElement commandElement = stanza.getSingleInnerElementsNamed("command");
if (commandElement == null) {
return ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST, stanza,
StanzaErrorType.MODIFY, "command is missing", null, null);
}
commandNode = commandElement.getAttributeValue("node");
requestedSessionId = commandElement.getAttributeValue("sessionid");
action = commandElement.getAttributeValue("action");
if (StringUtils.isEmpty(requestedSessionId)) {
for (AdhocCommandSupport commandSupport : adhocCommandSupporters) {
commandHandler = commandSupport.getCommandHandler(commandNode, from);
if (commandHandler != null) {
runningCommands.put(commandHandler.getSessionId(), commandHandler);
break;
}
}
} else {
commandHandler = runningCommands.get(requestedSessionId);
if (commandHandler == null) {
return ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST, stanza,
StanzaErrorType.CANCEL, "command session id not found: " + requestedSessionId, null, null);
}
}
commandElements = commandElement.getInnerElements();
} catch (XMLSemanticError xmlSemanticError) {
return ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST, stanza,
StanzaErrorType.MODIFY, "command is not well-formed", null, null);
}
if ("cancel".equals(action)) {
runningCommands.remove(requestedSessionId);
return buildResponse(stanza, from, commandNode, requestedSessionId, "canceled");
}
// handle unauthorized access (or command does not exist at all)
if (commandHandler == null) {
return ServerErrorResponses.getStanzaError(StanzaErrorCondition.FORBIDDEN, stanza,
StanzaErrorType.CANCEL, "command is not available", null, null);
}
List<Note> notes = new ArrayList<Note>();
final XMLElement result = commandHandler.process(commandElements, notes);
final String sessionId = commandHandler.getSessionId();
final boolean isExecuting = commandHandler.isExecuting();
final Stanza response = buildResponse(stanza, from, commandNode, sessionId,
isExecuting ? "executing" : "completed", result, notes,
commandHandler.isPrevAllowed(), commandHandler.isNextAllowed());
return response;
}
private Stanza buildResponse(IQStanza stanza, Entity from, String commandNode, String sessionId,
final String status) {
return buildResponse(stanza, from, commandNode, sessionId, status, null, null, false, false);
}
private Stanza buildResponse(IQStanza stanza, Entity from, String commandNode, String sessionId,
final String status, XMLElement result,
List<Note> notes, boolean isPrevAllowed, boolean isNextAllowed) {
final StanzaBuilder iqStanza = StanzaBuilder.createIQStanza(null, from, IQStanzaType.RESULT, stanza.getID());
iqStanza.startInnerElement("command");
iqStanza.declareNamespace("", NamespaceURIs.XEP0050_ADHOC_COMMANDS);
iqStanza.addAttribute("node", commandNode);
iqStanza.addAttribute("sessionid", sessionId);
iqStanza.addAttribute("status", status);
if (notes != null && notes.size() > 0) {
for (Note note : notes) {
iqStanza.startInnerElement("note");
iqStanza.addAttribute("type", note.getType().name());
if (note.getText() != null) iqStanza.addText(note.getText());
iqStanza.endInnerElement();
}
}
if (isNextAllowed || isPrevAllowed) {
iqStanza.startInnerElement("action");
if (isPrevAllowed) iqStanza.startInnerElement("prev").endInnerElement();
if (isNextAllowed) iqStanza.startInnerElement("next").endInnerElement();
iqStanza.endInnerElement();
}
if (result != null) {
iqStanza.addPreparedElement(result);
}
iqStanza.endInnerElement();
return iqStanza.build();
}
/*
<iq from='shakespeare.lit'
id='add-user-1'
to='bard@shakespeare.lit/globe'
type='result'
xml:lang='en'>
<command xmlns='http://jabber.org/protocol/commands'
node='http://jabber.org/protocol/admin#add-user'
sessionid='add-user:20040408T0337Z'
status='executing'>
<x xmlns='jabber:x:data' type='form'>
<title>Adding a User</title>
<instructions>Fill out this form to add a user.</instructions>
<field type='hidden' var='FORM_TYPE'>
<value>http://jabber.org/protocol/admin</value>
</field>
<field label='The Jabber ID for the account to be added'
type='jid-single'
var='accountjid'>
<required/>
</field>
<field label='The password for this account'
type='text-private'
var='password'/>
<field label='Retype password'
type='text-private'
var='password-verify'/>
<field label='Email address'
type='text-single'
var='email'/>
<field label='Given name'
type='text-single'
var='given_name'/>
<field label='Family name'
type='text-single'
var='surname'/>
</x>
</command>
</iq>
*/
@Override
protected Stanza handleGet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
Entity to = stanza.getTo();
Entity from = stanza.getFrom();
if (from == null) {
from = sessionContext.getInitiatingEntity();
}
StanzaBuilder stanzaBuilder = StanzaBuilder.createIQStanza(stanza.getTo(), stanza.getFrom(),
IQStanzaType.RESULT, stanza.getID());
return stanzaBuilder.build();
}
}