/* * 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.console; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.vysper.xmpp.addressing.Entity; import org.apache.vysper.xmpp.addressing.EntityImpl; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smackx.commands.AdHocCommand.Action; import org.jivesoftware.smackx.commands.AdHocCommand.Status; import org.jivesoftware.smackx.commands.AdHocCommandNote; import org.jivesoftware.smackx.packet.AdHocCommandData; import org.jivesoftware.smackx.packet.DataForm; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; /** * MVC controller * * @author The Apache MINA Project (dev@mina.apache.org) */ @Controller public class AdminConsoleController { private static final String MODEL_AUTHENTICATED = "authenticated"; public static final String SESSION_ATTRIBUTE = "smack.client"; public static final String SESSION_FIELD = "vysper-admingui-sessionid"; public static final Map<String, String> COMMANDS = new HashMap<String, String>(); static { COMMANDS.put("get-online-users-num", "Get online users"); COMMANDS.put("add-user", "Add user"); COMMANDS.put("change-user-password", "Change user password"); } private ConnectionConfiguration connectionConfiguration; private AdHocCommandDataBuilder adHocCommandDataBuilder = new AdHocCommandDataBuilder(); private HtmlFormBuilder htmlFormBuilder = new HtmlFormBuilder(); @Autowired public AdminConsoleController(ConnectionConfiguration connectionConfiguration) { this.connectionConfiguration = connectionConfiguration; } /** * Show the first page */ @RequestMapping("/") public ModelAndView index(HttpSession session) throws XMPPException { ExtendedXMPPConnection client = (ExtendedXMPPConnection) session.getAttribute(SESSION_ATTRIBUTE); if(client == null) { // login return login(); } else if(!client.isConnected()) { return login("Disconnected from XMPP server, please log in again"); } else { ModelAndView mav = new ModelAndView("index"); mav.addObject(MODEL_AUTHENTICATED, getUserName(client)); return mav; } } private String getUserName(XMPPConnection client) { Entity entity = EntityImpl.parseUnchecked(client.getUser()); return entity.getBareJID().getFullQualifiedName(); } /** * Show the initial command form */ @RequestMapping(value="/{command}", method=RequestMethod.GET) public ModelAndView command(@PathVariable("command") String command, HttpSession session) throws XMPPException { ExtendedXMPPConnection client = (ExtendedXMPPConnection) session.getAttribute(SESSION_ATTRIBUTE); if(client == null) { // login return login(); } else if(!client.isConnected()) { return login("Disconnected from XMPP server, please log in again"); } else { if(!COMMANDS.keySet().contains(command)) { throw new ResourceNotFoundException(); } AdHocCommandData requestCommand = new AdHocCommandData(); requestCommand.setType(Type.SET); requestCommand.setFrom(client.getUser()); requestCommand.setTo(client.getServiceName()); requestCommand.setAction(Action.execute); requestCommand.setNode("http://jabber.org/protocol/admin#" + command); return sendRequestAndGenerateForm(command, client, requestCommand); } } /** * Handle a submitted form and show the result or additional form */ @RequestMapping(value="/{command}", method=RequestMethod.POST) public ModelAndView submitCommand(@PathVariable("command") String command, HttpServletRequest request, HttpSession session) throws XMPPException { ExtendedXMPPConnection client = (ExtendedXMPPConnection) session.getAttribute(SESSION_ATTRIBUTE); if(client == null) { // login return login(); } else if(!client.isConnected()) { return login("Disconnected from XMPP server, please log in again"); } else { @SuppressWarnings("unchecked") AdHocCommandData requestCommand = adHocCommandDataBuilder.build(request.getParameterMap()); requestCommand.setType(Type.SET); requestCommand.setFrom(client.getUser()); requestCommand.setTo(client.getServiceName()); requestCommand.setNode("http://jabber.org/protocol/admin#" + command); return sendRequestAndGenerateForm(command, client, requestCommand); } } private ModelAndView sendRequestAndGenerateForm(String command, ExtendedXMPPConnection client, AdHocCommandData requestCommand) { try { Packet response = client.sendSync(requestCommand); StringBuffer htmlForm = new StringBuffer(); if(response != null) { AdHocCommandData responseData = (AdHocCommandData) response; DataForm form = responseData.getForm(); for(AdHocCommandNote note : responseData.getNotes()) { htmlForm.append("<p class='note " + note.getType() + "'>" + note.getValue() + "</p>"); } htmlForm.append("<form action='' method='post'>"); htmlForm.append("<input type='hidden' name='" + SESSION_FIELD + "' value='" + responseData.getSessionID() + "' />"); htmlForm.append(htmlFormBuilder.build(form)); if(Status.executing.equals(responseData.getStatus())) { htmlForm.append("<input type='submit' value='" + COMMANDS.get(command) + "' />"); } else if(Status.completed.equals(responseData.getStatus())) { if(form == null || form.getFields() == null || !form.getFields().hasNext()) { // no field, print success htmlForm.append("<p>Command successful</p>"); } } htmlForm.append("</form>"); } else { htmlForm.append("<p class='note error'>Timeout waiting for response from XMPP server</p>"); } ModelAndView mav = new ModelAndView("command"); mav.addObject(MODEL_AUTHENTICATED, getUserName(client)); mav.addObject("form", htmlForm.toString()); return mav; } catch (InterruptedException e) { throw new RuntimeException(e); } } private ModelAndView login() { return login("Please log in"); } private ModelAndView login(String msg) { ModelAndView mav = new ModelAndView("index"); mav.addObject("message", msg); return mav; } protected ExtendedXMPPConnection createXMPPConnection() { return new ExtendedXMPPConnection(connectionConfiguration); } /** * Connect and authenticate the user */ @RequestMapping(value="/login", method=RequestMethod.POST) public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) { ExtendedXMPPConnection client = createXMPPConnection(); try { client.connect(); client.login(username, password); session.setAttribute(SESSION_ATTRIBUTE, client); return new ModelAndView("redirect:"); } catch (XMPPException e) { ModelAndView mav = new ModelAndView("index"); mav.addObject("error", "Failed to login to server: " + e.getMessage()); return mav; } } /** * Log out and disconnect the user */ @RequestMapping(value="/logout") public ModelAndView logout(HttpSession session) { ExtendedXMPPConnection client = (ExtendedXMPPConnection) session.getAttribute(SESSION_ATTRIBUTE); if(client != null) { client.disconnect(); session.removeAttribute(SESSION_ATTRIBUTE); } return new ModelAndView("redirect:"); } }