Merge branch 'master' of https://github.com/bigbluebutton/bigbluebutton into improving-client-checks

This commit is contained in:
Felipe Cecagno 2014-11-12 10:38:00 -02:00
commit 535604b8b6
8 changed files with 181 additions and 238 deletions

View File

@ -21,3 +21,5 @@
<a href="demo11.jsp">Javascript API</a> &nbsp;&nbsp;
<a href="mobile.jsp">Mobile Demo</a> &nbsp;&nbsp

140
bbb-api-demo/src/main/webapp/mobile.jsp Normal file → Executable file
View File

@ -1,51 +1,119 @@
<%
/*
BigBlueButton - http://www.bigbluebutton.org
<!--
Copyright (c) 2011 by respective authors (see below). All rights reserved.
BigBlueButton - http://www.bigbluebutton.org
BigBlueButton 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 of the License, or (at your option) any later
version.
Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
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.
BigBlueButton 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 of the License, or (at your option) any later
version.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
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.
Author: Felipe Cecagno <fcecagno@gmail.com>
*/
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
Author: Chad Pilkey <capilkey@gmail.org>
-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Join Demo Meeting From Mobile</title>
</head>
<%@page import="org.apache.commons.httpclient.HttpClient"%>
<%@page import="org.apache.commons.httpclient.HttpMethod"%>
<%@page import="org.apache.commons.httpclient.methods.GetMethod"%>
<body>
<%@ include file="mobile_api.jsp"%>
<%@ include file="bbb_api.jsp"%>
<%
String result = error(E_INVALID_URL);
if (request.getParameterMap().isEmpty()) {
result = success("mobileSupported", "This server supports mobile devices.");
} else if (request.getParameter("action") == null) {
// return the default result
} else if (request.getParameter("action").equals("getTimestamp")) {
result = getTimestamp(request);
} else if (request.getParameter("action").equals("getMeetings")) {
result = mobileGetMeetings(request);
} else if (request.getParameter("action").equals("join")) {
result = mobileJoinMeeting(request);
} else if (request.getParameter("action").equals("create")) {
result = mobileCreate(request);
}
if (request.getParameterMap().isEmpty()) {
//
// Assume we want to create a meeting
//
%>
<%@ include file="demo_header.jsp"%>
<h2>Join Demo Meeting From Mobile</h2>
You must have the BigBlueButton mobile client installed on your device for this demo to work.
<br /><br />
<FORM NAME="form1" METHOD="GET">
<table cellpadding="5" cellspacing="5" style="width: 400px; ">
<tbody>
<tr>
<td>
&nbsp;</td>
<td style="text-align: right; ">
Full Name:</td>
<td style="width: 5px; ">
&nbsp;</td>
<td style="text-align: left ">
<input type="text" autofocus required name="username" /></td>
</tr>
<tr>
<td>
&nbsp;</td>
<td>
&nbsp;</td>
<td>
&nbsp;</td>
<td>
<input type="submit" value="Join" /></td>
</tr>
</tbody>
</table>
<INPUT TYPE=hidden NAME=action VALUE="create">
</FORM>
<%
} else if (request.getParameter("action").equals("create")) {
//
// Got an action=create
//
String username = request.getParameter("username");
String url = BigBlueButtonURL.replace("bigbluebutton/","demo/");
// String preUploadPDF = "<?xml version='1.0' encoding='UTF-8'?><modules><module name='presentation'><document url='"+url+"pdfs/sample.pdf'/></module></modules>";
String joinURL = getJoinURL(request.getParameter("username"), "Demo Meeting", "false", null, null, null);
if (joinURL.startsWith("http://")) {
joinURL = joinURL.replace("http", "bigbluebutton");
%>
<%=result%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";
</script>
<%
} else {
%>
Error: getJoinURL() failed
<p/>
<%=joinURL %>
<%
}
}
%>
<%@ include file="demo_footer.jsp"%>
</body>
</html>

View File

@ -1,182 +0,0 @@
<%
/*
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2011 by respective authors (see below). All rights reserved.
BigBlueButton 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 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, If not, see <http://www.gnu.org/licenses/>.
Author: Felipe Cecagno <fcecagno@gmail.com>
*/
%>
<%@ include file="bbb_api.jsp"%>
<%@ include file="mobile_conf.jsp"%>
<%@page import="org.apache.commons.httpclient.HttpClient"%>
<%@page import="org.apache.commons.httpclient.HttpMethod"%>
<%@page import="org.apache.commons.httpclient.methods.GetMethod"%>
<%!
public final int E_OK = 0;
public final int E_CHECKSUM_NOT_INFORMED = 1;
public final int E_INVALID_CHECKSUM = 2;
public final int E_INVALID_TIMESTAMP = 3;
public final int E_EMPTY_SECURITY_KEY = 4;
public final int E_MISSING_PARAM_MEETINGID = 5;
public final int E_MISSING_PARAM_FULLNAME = 6;
public final int E_MISSING_PARAM_PASSWORD = 7;
public final int E_MISSING_PARAM_TIMESTAMP = 8;
public final int E_INVALID_URL = 9;
public String error(int code) {
switch(code) {
case E_CHECKSUM_NOT_INFORMED:
case E_INVALID_CHECKSUM:
return error("checksumError", "You did not pass the checksum security check.");
case E_INVALID_TIMESTAMP:
return error("invalidTimestamp", "You did not pass the timestamp check.");
case E_EMPTY_SECURITY_KEY:
return error("emptySecurityKey", "The mobile security key is empty. Please contact the administrator.");
case E_MISSING_PARAM_MEETINGID:
return error("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
case E_MISSING_PARAM_FULLNAME:
return error("missingParamFullName", "You must specify a name for the attendee who will be joining the meeting.");
case E_MISSING_PARAM_PASSWORD:
return error("invalidPassword", "You either did not supply a password or the password supplied is neither the attendee or moderator password for this conference.");
case E_MISSING_PARAM_TIMESTAMP:
return error("missingParamTimestamp", "You must specify the timestamp provided by the server when you called the method getTimestamp.");
case E_INVALID_URL:
return error("invalidAction", "The requested URL is unavailable.");
default:
return error("unknownError", "An unexpected error occurred");
}
}
private String getRequestURL(HttpServletRequest request) {
return request.getQueryString();
}
private String removeChecksum(String requestURL) {
return requestURL.substring(0, requestURL.lastIndexOf("&checksum="));
}
private String addValidChecksum(String requestURL) {
return requestURL + "&checksum=" + checksum(requestURL + mobileSalt);
}
private int isRequestValid(HttpServletRequest request) {
// if there's no checksum parameter, the request isn't valid
if (request.getParameter("checksum") == null)
return E_CHECKSUM_NOT_INFORMED;
// check the timestamp for all the requests except getTimestamp
if (!request.getParameter("action").equals("getTimestamp")) {
String requestTimestamp = request.getParameter("timestamp");
if (requestTimestamp == null)
return E_MISSING_PARAM_TIMESTAMP;
Long requestTimestampL = Long.valueOf(requestTimestamp);
// the timestamp is valid for 60 seconds
if (Math.abs(getTimestamp() - requestTimestampL) > 60)
return E_INVALID_TIMESTAMP;
}
if (mobileSalt.isEmpty())
return E_EMPTY_SECURITY_KEY;
String requestURL = getRequestURL(request);
String urlWithoutChecksum = removeChecksum(requestURL);
String urlWithChecksum = addValidChecksum(urlWithoutChecksum);
return (requestURL.equals(urlWithChecksum)? E_OK: E_INVALID_CHECKSUM);
}
private String mountResponse(String returncode, String messageKey, String message) {
return "<response><returncode>" + returncode + "</returncode><messageKey>" + messageKey + "</messageKey><message>" + message + "</message></response>";
}
public String success(String messageKey, String message) {
return mountResponse("SUCCESS", messageKey, message);
}
public String error(String messageKey, String message) {
return mountResponse("FAILED", messageKey, message);
}
private long getTimestamp() {
return (System.currentTimeMillis() / 1000L);
}
public String getTimestamp(HttpServletRequest request) {
int code = isRequestValid(request);
if (code != E_OK)
return error(code);
return "<response><returncode>SUCCESS</returncode><timestamp>" + getTimestamp() + "</timestamp></response>";
}
public String mobileGetMeetings(HttpServletRequest request) {
int code = isRequestValid(request);
if (code != E_OK)
return error(code);
return getMeetings();
}
public String mobileJoinMeeting(HttpServletRequest request) {
int code = isRequestValid(request);
if (code != E_OK)
return error(code);
String meetingID = request.getParameter("meetingID");
if (meetingID == null) return error(E_MISSING_PARAM_MEETINGID);
String fullName = request.getParameter("fullName");
if (fullName == null) return error(E_MISSING_PARAM_FULLNAME);
String password = request.getParameter("password");
if (password == null) return error(E_MISSING_PARAM_PASSWORD);
String result = error("failedJoin", "Couldn't join the meeting.");
String joinUrl = getJoinMeetingURL(fullName, meetingID, password, null);
String enterUrl = BigBlueButtonURL + "api/enter";
try {
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod(joinUrl);
client.executeMethod(method);
method.releaseConnection();
method = new GetMethod(enterUrl);
client.executeMethod(method);
result = method.getResponseBodyAsString();
method.releaseConnection();
} catch (Exception e) {
}
return result;
}
public String mobileCreate(HttpServletRequest request) {
int code = isRequestValid(request);
if (code != E_OK)
return error(code);
String meetingID = request.getParameter("meetingID");
if (meetingID == null) return error(E_MISSING_PARAM_MEETINGID);
return createMeeting(meetingID, "", "", "", "", 0, BigBlueButtonURL);
}
// it's just for testing purposes
private String fixURL(HttpServletRequest request) {
return addValidChecksum(removeChecksum(getRequestURL(request)));
}
%>

View File

@ -1,6 +0,0 @@
<%!
// This is the mobile security salt that must be used to check the requests on mobile.jsp
String mobileSalt = "";
%>

View File

@ -19,6 +19,11 @@
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.bigbluebutton.voiceconf.red5.media.FlashToSipAudioStream.TranscodedAudioListener;
import org.red5.app.sip.codecs.Codec;
@ -36,9 +41,16 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
private Codec audioCodec;
private long timestamp = 0;
private final static int TS_INCREMENT = 320; // Determined from PCAP traces.
private final Executor exec = Executors.newSingleThreadExecutor();
private Runnable audioDataProcessor;
private volatile boolean processAudioData = false;
private BlockingQueue<SpeexRtpAudioData> audioDataQ;
private TranscodedAudioListener transcodedAudioListener;
public SpeexFlashToSipTranscoderImp(Codec audioCodec) {
audioDataQ = new LinkedBlockingQueue<SpeexRtpAudioData>();
this.audioCodec = audioCodec;
Random rgen = new Random();
timestamp = rgen.nextInt(1000);
@ -49,7 +61,13 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
// Just copy the audio data removing the codec id which is the first-byte
// represented by the startOffset var.
System.arraycopy(audioData, startOffset, transcodedAudio, 0, length);
transcodedAudioListener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
SpeexRtpAudioData srad = new SpeexRtpAudioData(transcodedAudio, timestamp += TS_INCREMENT);
try {
audioDataQ.offer(srad, 100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.warn("Failed to add speex audio data into queue.");
}
}
public int getCodecId() {
@ -74,13 +92,32 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
this.transcodedAudioListener = transcodedAudioListener;
}
private void processAudioData() {
while (processAudioData) {
SpeexRtpAudioData srad;
try {
srad = audioDataQ.take();
transcodedAudioListener.handleTranscodedAudioData(srad.audioData, srad.timestamp);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void start() {
// do nothing. just implement the interface.
processAudioData = true;
audioDataProcessor = new Runnable() {
public void run() {
processAudioData();
}
};
exec.execute(audioDataProcessor);
}
@Override
public void stop() {
// do nothing. just implement the interface.
processAudioData = false;
}
}

View File

@ -0,0 +1,12 @@
package org.bigbluebutton.voiceconf.red5.media.transcoder;
public class SpeexRtpAudioData {
public final byte[] audioData;
public final long timestamp;
public SpeexRtpAudioData(byte[] audioData, long timestamp) {
this.audioData = audioData;
this.timestamp = timestamp;
}
}

View File

@ -2526,6 +2526,15 @@ var Hacks = module.exports = {
}
}
return sdp;
},
hackCLinInIP: function (sdp) {
/* Starting in Firefox 34 they have set the c line to 0.0.0.0 which
* means "hold" according to legacy SIP standards and Freeswitch
* interprets it according to the SIP standards. We replace the
* 0.0.0.0 with any other IP so that the call continues.
*/
return sdp.replace("c=IN IP4 0.0.0.0", "c=IN IP4 127.0.0.1");
}
},
@ -6036,6 +6045,9 @@ InviteClientContext.prototype = {
if (self.isCanceled || self.status === C.STATUS_TERMINATED) {
return;
}
offer = SIP.Hacks.Firefox.hackCLinInIP(offer);
self.hasOffer = true;
self.request.body = offer;
self.status = C.STATUS_INVITE_SENT;

View File

@ -23,7 +23,7 @@ package org.bigbluebutton.clientcheck.command
[Inject]
public var config:IXMLConfig;
private var CONFIG_XML:String="client/client-check/conf/config.xml";
private var CONFIG_XML:String="check/conf/config.xml";
private var _urlRequest:URLRequest;
public override function execute():void