384 lines
17 KiB
Groovy
384 lines
17 KiB
Groovy
/*
|
|
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
|
|
|
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU Lesser General Public License as published by the Free Software
|
|
Foundation; either version 3.0 of the License, or (at your option) any later
|
|
version.
|
|
|
|
BigBlueButton 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 Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.StringReader;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.URL;
|
|
import java.text.MessageFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
|
|
import org.apache.commons.codec.digest.DigestUtils;
|
|
|
|
import org.bigbluebutton.api.Proxy
|
|
import org.bigbluebutton.lti.Role
|
|
import org.bigbluebutton.lti.Parameter
|
|
|
|
class BigbluebuttonService {
|
|
|
|
boolean transactional = false
|
|
|
|
def url = "http://test-install.blindsidenetworks.com/bigbluebutton"
|
|
def salt = "8cd8ef52e8e101574e400365b55e11a6"
|
|
|
|
Proxy bbbProxy
|
|
DocumentBuilderFactory docBuilderFactory
|
|
DocumentBuilder docBuilder
|
|
|
|
BigbluebuttonService(){
|
|
// Initialize XML libraries
|
|
docBuilderFactory = DocumentBuilderFactory.newInstance()
|
|
try {
|
|
docBuilder = docBuilderFactory.newDocumentBuilder()
|
|
} catch (ParserConfigurationException e) {
|
|
logger.error("Failed to initialise BaseProxy", e)
|
|
}
|
|
|
|
//Instantiate bbbProxy and initialize it with default url and salt
|
|
bbbProxy = new Proxy(url, salt)
|
|
|
|
}
|
|
|
|
public String getJoinURL(params, welcome, mode){
|
|
//Set the injected values
|
|
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
|
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
|
|
|
String joinURL = null
|
|
|
|
String meetingName = getValidatedMeetingName(params.get(Parameter.RESOURCE_LINK_TITLE))
|
|
String meetingID = getValidatedMeetingId(params.get(Parameter.RESOURCE_LINK_ID), params.get(Parameter.CONSUMER_ID))
|
|
String attendeePW = DigestUtils.shaHex("ap" + params.get(Parameter.RESOURCE_LINK_ID) + params.get(Parameter.CONSUMER_ID))
|
|
String moderatorPW = DigestUtils.shaHex("mp" + params.get(Parameter.RESOURCE_LINK_ID) + params.get(Parameter.CONSUMER_ID))
|
|
String logoutURL = getValidatedLogoutURL(params.get(Parameter.LAUNCH_RETURN_URL))
|
|
boolean isModerator = params.get(Parameter.ROLES) != null? Role.isModerator(params.get(Parameter.ROLES)): true
|
|
String userFullName = getValidatedUserFullName(params, isModerator)
|
|
String courseTitle = getValidatedCourseTitle(params.get(Parameter.COURSE_TITLE))
|
|
String userID = getValidatedUserId(params.get(Parameter.USER_ID))
|
|
|
|
Integer voiceBridge = 0
|
|
Boolean record = false
|
|
Integer duration = 0
|
|
if( "extended".equals(mode) ){
|
|
voiceBridge = getValidatedBBBVoiceBridge(params.get(Parameter.CUSTOM_BBB_VOICEBRIDGE))
|
|
record = getValidatedBBBRecord(params.get(Parameter.CUSTOM_BBB_RECORD))
|
|
duration = getValidatedBBBDuration(params.get(Parameter.CUSTOM_BBB_DURATION))
|
|
}
|
|
|
|
String[] values = [meetingName, courseTitle]
|
|
String welcomeMsg = MessageFormat.format(welcome, values)
|
|
|
|
String meta = getMonitoringMetaData(params)
|
|
|
|
String createURL = getCreateURL( meetingName, meetingID, attendeePW, moderatorPW, welcomeMsg, voiceBridge, logoutURL, record, duration, meta )
|
|
log.debug "createURL: " + createURL
|
|
Map<String, Object> createResponse = doAPICall(createURL)
|
|
log.debug "createResponse: " + createResponse
|
|
|
|
if( createResponse != null){
|
|
String returnCode = (String) createResponse.get("returncode")
|
|
String messageKey = (String) createResponse.get("messageKey")
|
|
if ( Proxy.APIRESPONSE_SUCCESS.equals(returnCode) ||
|
|
(Proxy.APIRESPONSE_FAILED.equals(returnCode) && (Proxy.MESSAGEKEY_IDNOTUNIQUE.equals(messageKey) || Proxy.MESSAGEKEY_DUPLICATEWARNING.equals(messageKey)) ) ){
|
|
joinURL = bbbProxy.getJoinMeetingURL( userFullName, meetingID, isModerator? moderatorPW: attendeePW, (String) createResponse.get("createTime"), userID);
|
|
}
|
|
}
|
|
|
|
return joinURL
|
|
|
|
}
|
|
|
|
public Object getRecordings(params){
|
|
//Set the injected values
|
|
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
|
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
|
|
|
String meetingID = getValidatedMeetingId(params.get(Parameter.RESOURCE_LINK_ID), params.get(Parameter.CONSUMER_ID))
|
|
|
|
String recordingsURL = bbbProxy.getRecordingsURL( meetingID )
|
|
log.debug "recordingsURL: " + recordingsURL
|
|
Map<String, Object> recordings = doAPICall(recordingsURL)
|
|
|
|
if( recordings != null){
|
|
String returnCode = (String) recordings.get("returncode")
|
|
String messageKey = (String) recordings.get("messageKey")
|
|
if ( Proxy.APIRESPONSE_SUCCESS.equals(returnCode) && messageKey == null ){
|
|
return recordings.get("recordings")
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
public Object doDeleteRecordings(params){
|
|
//Set the injected values
|
|
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
|
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
|
|
|
Map<String, Object> result
|
|
|
|
String recordingId = getValidatedBBBRecordingId(params.get(Parameter.BBB_RECORDING_ID))
|
|
|
|
if( !recordingId.equals("") ){
|
|
String deleteRecordingsURL = bbbProxy.getDeleteRecordingsURL( recordingId )
|
|
log.debug "deleteRecordingsURL: " + deleteRecordingsURL
|
|
result = doAPICall(deleteRecordingsURL)
|
|
} else {
|
|
result = new HashMap<String, String>()
|
|
result.put("resultMessageKey", "InvalidRecordingId")
|
|
result.put("resultMessage", "RecordingId is invalid. The recording can not be deleted.")
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
public Object doPublishRecordings(params){
|
|
//Set the injected values
|
|
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
|
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
|
|
|
Map<String, Object> result
|
|
|
|
String recordingId = getValidatedBBBRecordingId(params.get(Parameter.BBB_RECORDING_ID))
|
|
String publish = getValidatedBBBRecordingPublished(params.get(Parameter.BBB_RECORDING_PUBLISHED))
|
|
|
|
if( !recordingId.equals("") ){
|
|
String publishRecordingsURL = bbbProxy.getPublishRecordingsURL( recordingId, "true".equals(publish)?"false":"true" )
|
|
log.debug "publishRecordingsURL: " + publishRecordingsURL
|
|
result = doAPICall(publishRecordingsURL)
|
|
} else {
|
|
result = new HashMap<String, String>()
|
|
result.put("resultMessageKey", "InvalidRecordingId")
|
|
result.put("resultMessage", "RecordingId is invalid. The recording can not be deleted.")
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
private String getCreateURL(String name, String meetingID, String attendeePW, String moderatorPW, String welcome, Integer voiceBridge, String logoutURL, Boolean record, Integer duration, String meta ) {
|
|
voiceBridge = ( voiceBridge == null || voiceBridge == 0 )? 70000 + new Random(System.currentTimeMillis()).nextInt(10000): voiceBridge;
|
|
|
|
String url = bbbProxy.getCreateURL(name, meetingID, attendeePW, moderatorPW, welcome, "", voiceBridge.toString(), "", logoutURL, "", record.toString(), duration.toString(), meta );
|
|
return url;
|
|
}
|
|
|
|
private String getValidatedMeetingName(String meetingName){
|
|
return (meetingName == null || meetingName == "")? "Meeting": meetingName
|
|
}
|
|
|
|
private String getValidatedMeetingId(String resourceId, String consumerId){
|
|
return DigestUtils.shaHex(resourceId + consumerId)
|
|
}
|
|
|
|
private String getValidatedLogoutURL(String logoutURL){
|
|
return (logoutURL == null)? "": logoutURL
|
|
}
|
|
|
|
private String getValidatedUserFullName(params, boolean isModerator){
|
|
String userFullName = params.get(Parameter.USER_FULL_NAME)
|
|
String userFirstName = params.get(Parameter.USER_FIRSTNAME)
|
|
String userLastName = params.get(Parameter.USER_LASTNAME)
|
|
if( userFullName == null || userFullName == "" ){
|
|
if( userFirstName != null && userFirstName != "" ){
|
|
userFullName = userFirstName
|
|
}
|
|
if( userLastName != null && userLastName != "" ){
|
|
userFullName = userFullName.length() > 0? " ": ""
|
|
userFullName += userLastName
|
|
}
|
|
if( userFullName == null || userFullName == "" ){
|
|
userFullName = isModerator? "Moderator" : "Attendee"
|
|
}
|
|
}
|
|
|
|
return userFullName
|
|
}
|
|
|
|
private String getValidatedCourseTitle(String courseTitle){
|
|
return (courseTitle == null || courseTitle == "")? "Course": courseTitle
|
|
}
|
|
|
|
private String getValidatedUserId(String userId){
|
|
return (userId == null)? "": userId
|
|
}
|
|
|
|
private Integer getValidatedBBBVoiceBridge(String voiceBridge){
|
|
return (voiceBridge != null )? voiceBridge.toInteger(): 0
|
|
}
|
|
|
|
private Boolean getValidatedBBBRecord(String record){
|
|
return (record != null && record == "true")? true: false
|
|
}
|
|
|
|
private Integer getValidatedBBBDuration(String duration){
|
|
return (duration != null )? duration.toInteger(): 0
|
|
}
|
|
|
|
private String getValidatedBBBRecordingId(String recordingId){
|
|
return (recordingId != null )? recordingId: ""
|
|
}
|
|
|
|
private String getValidatedBBBRecordingPublished(String published){
|
|
return (published != null && published.equals("true") )? "true": "false"
|
|
}
|
|
|
|
private String getMonitoringMetaData(params){
|
|
String meta
|
|
|
|
meta = "meta_origin=" + bbbProxy.getStringEncoded(params.get(Parameter.TOOL_CONSUMER_CODE) == null? "": params.get(Parameter.TOOL_CONSUMER_CODE))
|
|
meta += "&meta_originVersion=" + bbbProxy.getStringEncoded(params.get(Parameter.TOOL_CONSUMER_VERSION) == null? "": params.get(Parameter.TOOL_CONSUMER_VERSION))
|
|
meta += "&meta_originServerCommonName=" + bbbProxy.getStringEncoded(params.get(Parameter.TOOL_CONSUMER_INSTANCE_DESCRIPTION) == null? "": params.get(Parameter.TOOL_CONSUMER_INSTANCE_DESCRIPTION))
|
|
meta += "&meta_originServerUrl=" + bbbProxy.getStringEncoded(params.get(Parameter.TOOL_CONSUMER_INSTANCE_URL) == null? "": params.get(Parameter.TOOL_CONSUMER_INSTANCE_URL))
|
|
meta += "&meta_context=" + bbbProxy.getStringEncoded(params.get(Parameter.COURSE_TITLE) == null? "": params.get(Parameter.COURSE_TITLE))
|
|
meta += "&meta_contextId=" + bbbProxy.getStringEncoded(params.get(Parameter.COURSE_ID) == null? "": params.get(Parameter.COURSE_ID))
|
|
meta += "&meta_contextActivity=" + bbbProxy.getStringEncoded(params.get(Parameter.RESOURCE_LINK_TITLE) == null? "": params.get(Parameter.RESOURCE_LINK_TITLE))
|
|
meta += "&meta_contextActivityDescription=" + bbbProxy.getStringEncoded(params.get(Parameter.RESOURCE_LINK_DESCRIPTION) == null? "": params.get(Parameter.RESOURCE_LINK_DESCRIPTION))
|
|
|
|
return meta
|
|
}
|
|
|
|
/** Make an API call */
|
|
private Map<String, Object> doAPICall(String query) {
|
|
StringBuilder urlStr = new StringBuilder(query);
|
|
|
|
try {
|
|
// open connection
|
|
//log.debug("doAPICall.call: " + query );
|
|
|
|
URL url = new URL(urlStr.toString());
|
|
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
|
|
httpConnection.setUseCaches(false);
|
|
httpConnection.setDoOutput(true);
|
|
httpConnection.setRequestMethod("GET");
|
|
httpConnection.connect();
|
|
|
|
int responseCode = httpConnection.getResponseCode();
|
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
// read response
|
|
InputStreamReader isr = null;
|
|
BufferedReader reader = null;
|
|
StringBuilder xml = new StringBuilder();
|
|
try {
|
|
isr = new InputStreamReader(httpConnection.getInputStream(), "UTF-8");
|
|
reader = new BufferedReader(isr);
|
|
String line = reader.readLine();
|
|
while (line != null) {
|
|
if( !line.startsWith("<?xml version=\"1.0\"?>"))
|
|
xml.append(line.trim());
|
|
line = reader.readLine();
|
|
}
|
|
} finally {
|
|
if (reader != null)
|
|
reader.close();
|
|
if (isr != null)
|
|
isr.close();
|
|
}
|
|
httpConnection.disconnect();
|
|
|
|
// parse response
|
|
//log.debug("doAPICall.responseXml: " + xml);
|
|
//Patch to fix the NaN error
|
|
String stringXml = xml.toString();
|
|
stringXml = stringXml.replaceAll(">.\\s+?<", "><");
|
|
|
|
Document dom = null;
|
|
dom = docBuilder.parse(new InputSource( new StringReader(stringXml)));
|
|
|
|
Map<String, Object> response = getNodesAsMap(dom, "response");
|
|
//log.debug("doAPICall.responseMap: " + response);
|
|
|
|
String returnCode = (String) response.get("returncode");
|
|
if (Proxy.APIRESPONSE_FAILED.equals(returnCode)) {
|
|
log.debug("doAPICall." + (String) response.get("messageKey") + ": Message=" + (String) response.get("message"));
|
|
}
|
|
|
|
return response;
|
|
|
|
} else {
|
|
log.debug("doAPICall.HTTPERROR: Message=" + "BBB server responded with HTTP status code " + responseCode);
|
|
}
|
|
|
|
} catch(IOException e) {
|
|
log.debug("doAPICall.IOException: Message=" + e.getMessage());
|
|
} catch(SAXException e) {
|
|
log.debug("doAPICall.SAXException: Message=" + e.getMessage());
|
|
} catch(IllegalArgumentException e) {
|
|
log.debug("doAPICall.IllegalArgumentException: Message=" + e.getMessage());
|
|
} catch(Exception e) {
|
|
log.debug("doAPICall.Exception: Message=" + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/** Get all nodes under the specified element tag name as a Java map */
|
|
protected Map<String, Object> getNodesAsMap(Document dom, String elementTagName) {
|
|
Node firstNode = dom.getElementsByTagName(elementTagName).item(0);
|
|
return processNode(firstNode);
|
|
}
|
|
|
|
protected Map<String, Object> processNode(Node _node) {
|
|
Map<String, Object> map = new HashMap<String, Object>();
|
|
NodeList responseNodes = _node.getChildNodes();
|
|
for (int i = 0; i < responseNodes.getLength(); i++) {
|
|
Node node = responseNodes.item(i);
|
|
String nodeName = node.getNodeName().trim();
|
|
if (node.getChildNodes().getLength() == 1
|
|
&& ( node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.TEXT_NODE || node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) ) {
|
|
String nodeValue = node.getTextContent();
|
|
map.put(nodeName, nodeValue != null ? nodeValue.trim() : null);
|
|
|
|
} else if (node.getChildNodes().getLength() == 0
|
|
&& node.getNodeType() != org.w3c.dom.Node.TEXT_NODE
|
|
&& node.getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE) {
|
|
map.put(nodeName, "");
|
|
|
|
} else if ( node.getChildNodes().getLength() >= 1
|
|
&& node.getChildNodes().item(0).getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.TEXT_NODE
|
|
&& node.getChildNodes().item(0).getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE ) {
|
|
|
|
List<Object> list = new ArrayList<Object>();
|
|
for (int c = 0; c < node.getChildNodes().getLength(); c++) {
|
|
Node n = node.getChildNodes().item(c);
|
|
list.add(processNode(n));
|
|
}
|
|
map.put(nodeName, list);
|
|
|
|
} else {
|
|
map.put(nodeName, processNode(node));
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
}
|