Commit 54d7381f authored by daryl herzmann's avatar daryl herzmann

Merge pull request #142 from igniterealtime/ofmeet-plugin-0.0.5

ofmeet plugin - version 0.0.5
parents 3ea5d4a5 2f3a5036
......@@ -49,6 +49,14 @@
Openfire Meetings Plugin Changelog
</h1>
<p><b>0.0.5</b> -- Dec 15th, 2014</p>
<ul>
<li>Added support for clientcontrol plugin groupchat bookmarks and pdf urls</li>
<li>Added jicofo, server-side conference focus</li>
<li>Updated to latest Jitsi Meet and Jitsi Videobridge</li>
</ul>
<p><b>0.0.3</b> -- Dec 6th, 2014</p>
<ul>
......
......@@ -3,9 +3,9 @@
<plugin>
<class>org.jivesoftware.openfire.plugin.ofmeet.OfMeetPlugin</class>
<name>Openfire Meetings</name>
<description>Provides high quality, scalable video conferences</description>
<description>Provides high quality, scalable video conferences using Jitsi Meet and Jitsi Videobridge</description>
<author>Ignite Realtime</author>
<version>0.0.4</version>
<version>0.0.5</version>
<date>11/30/2014</date>
<minServerVersion>3.9.9</minServerVersion>
......
......@@ -59,9 +59,9 @@
<p>Ignite Realtime is pleased to announce "Openfire Meeting", a new plugin for Openfire that continues the development of the ofmeet web application which was part of the deprecated <a href="https://community.igniterealtime.org/community/support/jitsi_videobridge_plugin">Jitsi-videobridge plugin</a>.</p>
<p style="min-height: 8pt; padding: 0px;">&nbsp;</p><p><strong>PLEASE NOTE</strong> - You will need latest Openfire 3.10.0 to use this plugin. Use a nightly build or wait for the imminent official release.</p>
<p><a href="https://community.igniterealtime.org/servlet/JiveServlet/showImage/38-1730-22181/ofmeet1.png"><img alt="ofmeet1.png" height="272" src="https://community.igniterealtime.org/servlet/JiveServlet/downloadImage/38-1730-22181/432-272/ofmeet1.png" style="width:432px; height: 272.438709677419px;" width="432"/></a></p>
<p style="min-height: 8pt; padding: 0px;">&nbsp;</p><p>Openfire Meetings is a complete standalone plugin powered by Jitsi Videobridge. It does not depend on any other plugins</p><p style="min-height: 8pt; padding: 0px;">&nbsp;</p>
<p style="min-height: 8pt; padding: 0px;">&nbsp;</p><p>Openfire Meetings is a complete standalone plugin powered by Jitsi Videobridge. It does not depend on any other plugins. It can be enhanced by adding the optional client control and fastpath plugins. The client control plugin will enable the management and user provisioning of PDF presentatation urls and conference bookmarks. Users can select presentations and conferences from a pull down list.</p><p style="min-height: 8pt; padding: 0px;">&nbsp;</p>
<p><a href="https://community.igniterealtime.org/servlet/JiveServlet/showImage/38-1730-22275/ofmeet3.png"><img alt="ofmeet3.png" height="160" src="https://community.igniterealtime.org/servlet/JiveServlet/downloadImage/38-1730-22275/428-160/ofmeet3.png" style="width:428px; height: 160.325732899023px;" width="428"/></a></p>
<p style="min-height: 8pt; padding: 0px;">&nbsp;</p><p>The front-end we application is a combination of Candy and Jitsi Meet and enables the following features</p>
<p style="min-height: 8pt; padding: 0px;">&nbsp;</p><p>The front-end web application is a combination of Candy and Jitsi Meet and enables the following features</p>
<ul>
<li>Openfire user authentication directly from web browser for both Candy and Jitsi Meet</li>
<li>Audio, Video and Telephone (SIP) conferencing directly with Jitsi Meet and from Candy</li>
......
This diff is collapsed.
......@@ -21,7 +21,7 @@ var AudioLevels = (function(my) {
videoSpanId = 'participant_' + resourceJid;
}
videoSpan = document.getElementById(videoSpanId);
var videoSpan = document.getElementById(videoSpanId);
if (!videoSpan) {
if (resourceJid)
......@@ -35,8 +35,7 @@ var AudioLevels = (function(my) {
var audioLevelCanvas = $('#' + videoSpanId + '>canvas');
var videoSpaceWidth = $('#remoteVideos').width();
var thumbnailSize
= VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var thumbnailWidth = thumbnailSize[0];
var thumbnailHeight = thumbnailSize[1];
......@@ -84,6 +83,53 @@ var AudioLevels = (function(my) {
drawContext.clearRect (0, 0,
audioLevelCanvas.width, audioLevelCanvas.height);
drawContext.drawImage(canvasCache, 0, 0);
if(resourceJid === AudioLevels.LOCAL_LEVEL) {
if(!connection.emuc.myroomjid) {
return;
}
resourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
}
if(resourceJid === VideoLayout.getLargeVideoState().userResourceJid) {
AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
}
};
my.updateActiveSpeakerAudioLevel = function(audioLevel) {
var drawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
var r = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
var center = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + r) / 2;
// Save the previous state of the context.
drawContext.save();
drawContext.clearRect(0, 0, 300, 300);
// Draw a circle.
drawContext.arc(center, center, r, 0, 2 * Math.PI);
// Add a shadow around the circle
drawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
drawContext.shadowBlur = getShadowLevel(audioLevel);
drawContext.shadowOffsetX = 0;
drawContext.shadowOffsetY = 0;
// Fill the shape.
drawContext.fill();
drawContext.save();
drawContext.restore();
drawContext.arc(center, center, r, 0, 2 * Math.PI);
drawContext.clip();
drawContext.clearRect(0, 0, 277, 200);
// Restore the previous context state.
drawContext.restore();
};
/**
......@@ -94,7 +140,7 @@ var AudioLevels = (function(my) {
thumbnailHeight) {
audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
};
}
/**
* Draws the audio level canvas into the cached canvas object.
......@@ -143,7 +189,7 @@ var AudioLevels = (function(my) {
interfaceConfig.CANVAS_RADIUS,
interfaceConfig.SHADOW_COLOR,
shadowLevel);
};
}
/**
* Returns the shadow/glow level for the given audio level.
......@@ -164,7 +210,7 @@ var AudioLevels = (function(my) {
shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
}
return shadowLevel;
};
}
/**
* Returns the video span id corresponding to the given resourceJid or local
......@@ -180,7 +226,7 @@ var AudioLevels = (function(my) {
videoSpanId = 'participant_' + resourceJid;
return videoSpanId;
};
}
/**
* Indicates that the remote video has been resized.
......
......@@ -14,7 +14,8 @@ var Avatar = (function(my) {
}
users[jid] = id;
}
var url = getGravatarUrl(users[jid] || jid, 100, jid); // BAO
var thumbUrl = getGravatarUrl(users[jid] || jid, 100, jid); // BAO
var contactListUrl = getGravatarUrl(users[jid] || jid, null, jid);
var resourceJid = Strophe.getResourceFromJid(jid);
var thumbnail = $('#participant_' + resourceJid);
var avatar = $('#avatar_' + resourceJid);
......@@ -22,25 +23,25 @@ var Avatar = (function(my) {
// set the avatar in the settings menu if it is local user and get the
// local video container
if(jid === connection.emuc.myroomjid) {
$('#avatar').get(0).src = url;
$('#avatar').get(0).src = thumbUrl;
thumbnail = $('#localVideoContainer');
}
// set the avatar in the contact list
var contact = $('#' + resourceJid + '>img');
if(contact && contact.length > 0) {
contact.get(0).src = url;
contact.get(0).src = contactListUrl;
}
// set the avatar in the thumbnail
if(avatar && avatar.length > 0) {
avatar[0].src = url;
avatar[0].src = thumbUrl;
} else {
if (thumbnail && thumbnail.length > 0) {
avatar = document.createElement('img');
avatar.id = 'avatar_' + resourceJid;
avatar.className = 'userAvatar';
avatar.src = url;
avatar.src = thumbUrl;
thumbnail.append(avatar);
}
}
......@@ -72,10 +73,11 @@ var Avatar = (function(my) {
}
//if the user is the currently focused, the dominant speaker or if
//there is no focused and no dominant speaker
if (activeSpeakerJid === jid) {
//there is no focused and no dominant speaker and the large video is
//currently shown
if (activeSpeakerJid === jid && VideoLayout.isLargeVideoOnTop()) {
setVisibility($("#largeVideo"), !show);
setVisibility($('#activeSpeakerAvatar'), show);
setVisibility($('#activeSpeaker'), show);
setVisibility(avatar, false);
setVisibility(video, false);
} else {
......@@ -93,12 +95,8 @@ var Avatar = (function(my) {
*/
my.updateActiveSpeakerAvatarSrc = function(jid) {
if(!jid) {
if (focusedVideoSrc) {
jid = getJidFromVideoSrc(focusedVideoSrc);
} else {
jid = connection.emuc.findJidFromResource(
VideoLayout.getDominantSpeakerResourceJid());
}
jid = connection.emuc.findJidFromResource(
VideoLayout.getLargeVideoState().userResourceJid);
}
var avatar = $("#activeSpeakerAvatar")[0];
var url = getGravatarUrl(users[jid],
......@@ -137,8 +135,8 @@ var Avatar = (function(my) {
return mediaStreams[jid][MediaStream.VIDEO_TYPE].muted;
}
function getGravatarUrl(id, size, jid) {
function getGravatarUrl(id, size, jid) { // BAO
if (connection.emuc.myroomjid == jid && config.userAvatar && config.userAvatar != "null")
{
return config.userAvatar; // BAO openfire avatars
......@@ -147,12 +145,14 @@ var Avatar = (function(my) {
return connection.ofmuc.members[jid].avatar;
}
if(id === connection.emuc.myroomjid || !id) {
id = SettingsMenu.getUID();
}
return '//www.gravatar.com/avatar/' + MD5.hexdigest(id.trim().toLowerCase()) + "?d=mm&size=" + (size || "30");
return 'https://www.gravatar.com/avatar/' +
MD5.hexdigest(id.trim().toLowerCase()) +
"?d=mm&size=" + (size || "30");
}
return my;
......
......@@ -2,9 +2,10 @@ var config = {
hosts: {
domain: 'btg199251',
//anonymousdomain: 'guest.example.com',
muc: 'conference.btg199251', // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.btg199251', // FIXME: use XEP-0030
//call_control: 'callcontrol.jitsi-meet.example.com'
muc: 'conference.jitsi-meet.example.com', // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.jitsi-meet.example.com', // FIXME: use XEP-0030
//call_control: 'callcontrol.jitsi-meet.example.com',
//focus: 'focus.jitsi-meet.example.com' - defaults to 'focus.jitsi-meet.example.com'
},
getroomnode: function (path)
{
......@@ -30,6 +31,7 @@ var config = {
useNicks: false,
bosh: '//btg199251:7443/http-bind/', // FIXME: use xep-0156 for that
clientNode: 'http://jitsi.org/jitsimeet', // The name of client node advertised in XEP-0115 'c' stanza
//focusUserJid: 'focus@auth.jitsi-meet.example.com', // The real JID of focus participant - can be overridden here
//defaultSipNumber: '', // Default SIP number
desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
......@@ -45,6 +47,7 @@ var config = {
enableRecording: false,
enableWelcomePage: true,
enableSimulcast: false,
enableFirefoxSupport: false //firefox support is still experimental, only one-to-one conferences with chrome focus
enableFirefoxSupport: false, //firefox support is still experimental, only one-to-one conferences with chrome focus
// will work when simulcast, bundle, mux, lastN and SCTP are disabled.
logStats: false // Enable logging of PeerConnection stats via the focus
};
......@@ -53,7 +53,7 @@ var ContactList = (function (my) {
}
};
newContact.appendChild(createAvatar(id, peerJid)); // BAO
newContact.appendChild(createAvatar(id, peerJid)); // BAO
newContact.appendChild(createDisplayNameParagraph("Participant"));
var clElement = contactlist.get(0);
......
......@@ -36,8 +36,6 @@
#remoteVideos .videocontainer {
display: inline-block;
background-color: black;
background-repeat: no-repeat;
background-position: 45;
background-size: contain;
border-radius:8px;
border: 2px solid #212425;
......@@ -376,6 +374,24 @@
position: absolute;
z-index: 0;
border-radius:10px;
pointer-events: none;
}
#activeSpeaker {
visibility: hidden;
width: 150px;
height: 150px;
margin: auto;
overflow: hidden;
position: relative;
}
#activeSpeakerAudioLevel {
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
visibility: inherit;
}
#mixedstream {
......@@ -383,12 +399,14 @@
}
#activeSpeakerAvatar {
visibility: hidden;
width: 100px;
height: 100px;
top: 25px;
margin: auto;
position: relative;
border-radius: 50px;
z-index: 2;
visibility: inherit;
}
.userAvatar {
......
......@@ -70,46 +70,65 @@ function onDataChannel(event)
'dominantspeakerchanged',
[dominantSpeakerEndpoint]);
}
else if ("InLastNChangeEvent" === colibriClass)
{
var oldValue = obj.oldValue;
var newValue = obj.newValue;
// Make sure that oldValue and newValue are of type boolean.
var type;
if ((type = typeof oldValue) !== 'boolean') {
if (type === 'string') {
oldValue = (oldValue == "true");
} else {
oldValue = new Boolean(oldValue).valueOf();
}
}
if ((type = typeof newValue) !== 'boolean') {
if (type === 'string') {
newValue = (newValue == "true");
} else {
newValue = new Boolean(newValue).valueOf();
}
}
$(document).trigger('inlastnchanged', [oldValue, newValue]);
}
else if ("LastNEndpointsChangeEvent" === colibriClass)
{
// The new/latest list of last-n endpoint IDs.
var lastNEndpoints = obj.lastNEndpoints;
/*
* The list of endpoint IDs which are entering the list of
* last-n at this time i.e. were not in the old list of last-n
* endpoint IDs.
*/
// The list of endpoint IDs which are entering the list of
// last-n at this time i.e. were not in the old list of last-n
// endpoint IDs.
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
var stream = obj.stream;
console.log(
"Data channel new last-n event: ",
lastNEndpoints, endpointsEnteringLastN, obj);
$(document).trigger(
'lastnchanged',
[lastNEndpoints, endpointsEnteringLastN, stream]);
'lastnchanged',
[lastNEndpoints, endpointsEnteringLastN, stream]);
}
else if ("SimulcastLayersChangedEvent" === colibriClass)
{
var endpointSimulcastLayers = obj.endpointSimulcastLayers;
$(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
$(document).trigger(
'simulcastlayerschanged',
[obj.endpointSimulcastLayers]);
}
else if ("SimulcastLayersChangingEvent" === colibriClass)
{
var endpointSimulcastLayers = obj.endpointSimulcastLayers;
$(document).trigger('simulcastlayerschanging', [endpointSimulcastLayers]);
$(document).trigger(
'simulcastlayerschanging',
[obj.endpointSimulcastLayers]);
}
else if ("StartSimulcastLayerEvent" === colibriClass)
{
var simulcastLayer = obj.simulcastLayer;
$(document).trigger('startsimulcastlayer', simulcastLayer);
$(document).trigger('startsimulcastlayer', obj.simulcastLayer);
}
else if ("StopSimulcastLayerEvent" === colibriClass)
{
var simulcastLayer = obj.simulcastLayer;
$(document).trigger('stopsimulcastlayer', simulcastLayer);
$(document).trigger('stopsimulcastlayer', obj.simulcastLayer);
}
else
{
......@@ -141,10 +160,8 @@ function bindDataChannelListener(peerConnection)
// and peer as single channel can be used for sending and receiving data.
// So either channel opened by the bridge or the one opened here is enough
// for communication with the bridge.
/*var dataChannelOptions =
{
reliable: true
};
/*
var dataChannelOptions = { reliable: true };
var dataChannel
= peerConnection.createDataChannel("myChannel", dataChannelOptions);
......@@ -157,6 +174,7 @@ function bindDataChannelListener(peerConnection)
{
var msgData = event.data;
console.info("Got My Data Channel Message:", msgData, dataChannel);
};*/
};
*/
}
......@@ -23,7 +23,7 @@ Description: WebRTC JavaScript video conferences
Package: jitsi-meet-prosody
Architecture: all
Pre-Depends: openssl, prosody | prosody-trunk, jitsi-videobridge
Depends: ${misc:Depends}
Depends: ${misc:Depends}, jicofo
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
......
......@@ -23,6 +23,8 @@ case "$1" in
. /etc/jitsi/videobridge/config
. /etc/jitsi/jicofo/config
# loading debconf
. /usr/share/debconf/confmodule
......@@ -33,17 +35,50 @@ case "$1" in
db_stop
PROSODY_CONFIG_PRESENT="true"
PROSODY_CREATE_JICOFO_USER="false"
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua"
PROSODY_CONFIG_OLD="/etc/prosody/prosody.cfg.lua"
# if there is no prosody config extract our template
# check for config in conf.avail or check whether it wasn't already configured in main config
if [ ! -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"$JVB_HOSTNAME\"" /etc/prosody/prosody.cfg.lua; then
if [ ! -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"$JVB_HOSTNAME\"" $PROSODY_CONFIG_OLD; then
PROSODY_CONFIG_PRESENT="false"
cp /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" $PROSODY_HOST_CONFIG
sed -i "s/jitmeetSecret/$JVB_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusSecret/$JICOFO_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusUser/$JICOFO_AUTH_USER/g" $PROSODY_HOST_CONFIG
if [ ! -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua ]; then
ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
fi
PROSODY_CREATE_JICOFO_USER="true"
fi
# UPGRADE to server side focus check if focus is configured
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"auth.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG; then
echo -e "\nVirtualHost \"auth.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
echo -e " authentication = \"internal_plain\"\n" >> $PROSODY_HOST_CONFIG
echo -e "admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n" >> $PROSODY_HOST_CONFIG
echo -e "Component \"focus.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
echo -e " component_secret=\"$JICOFO_SECRET\"\n" >> $PROSODY_HOST_CONFIG
PROSODY_CREATE_JICOFO_USER="true"
# UPGRADE to server side focus on old config(/etc/prosody/prosody.cfg.lua)
elif [ ! -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"auth.$JVB_HOSTNAME\"" $PROSODY_CONFIG_OLD; then
echo -e "\nVirtualHost \"auth.$JVB_HOSTNAME\"" >> $PROSODY_CONFIG_OLD
echo -e " authentication = \"internal_plain\"\n" >> $PROSODY_CONFIG_OLD
if ! grep -q "admins = { }" $PROSODY_CONFIG_OLD; then
echo -e "admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n" >> $PROSODY_CONFIG_OLD
else
sed -i "s/admins = { }/admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n/g" $PROSODY_CONFIG_OLD
fi
echo -e "Component \"focus.$JVB_HOSTNAME\"" >> $PROSODY_CONFIG_OLD
echo -e " component_secret=\"$JICOFO_SECRET\"\n" >> $PROSODY_CONFIG_OLD
PROSODY_CREATE_JICOFO_USER="true"
fi
if [ "$PROSODY_CREATE_JICOFO_USER" = "true" ]; then
# create 'focus@auth.domain' prosody user
prosodyctl register $JICOFO_AUTH_USER $JICOFO_AUTH_DOMAIN $JICOFO_AUTH_PASSWORD
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
......@@ -60,6 +95,7 @@ case "$1" in
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
invoke-rc.d prosody restart
invoke-rc.d jitsi-videobridge restart
invoke-rc.d jicofo restart
fi
;;
......
/* global $, config, connection, chrome, alert, getUserMediaWithConstraints, changeLocalVideo, getConferenceHandler */
/* global $, alert, changeLocalVideo, chrome, config, connection, getConferenceHandler, getUserMediaWithConstraints, VideoLayout */
/**
* Indicates that desktop stream is currently in use(for toggle purpose).
* @type {boolean}
......@@ -283,9 +283,9 @@ function toggleScreenSharing() {
}
switchInProgress = true;
// Only the focus is able to set a shared key.
if (!isUsingScreenStream)
{
// Switch to desktop stream
obtainDesktopStream(
function (stream) {
// We now use screen stream
......
......@@ -19,3 +19,11 @@ Component "conference.jitmeet.example.com" "muc"
Component "jitsi-videobridge.jitmeet.example.com"
component_secret = "jitmeetSecret"
VirtualHost "auth.jitmeet.example.com"
authentication = "internal_plain"
admins = { "focusUser@auth.jitmeet.example.com" }
Component "focus.jitmeet.example.com"
component_secret = "focusSecret"
/* global $, config, Prezi, Util, connection, setLargeVideoVisible, dockToolbar */
/* global $, config, connection, dockToolbar, Moderator, Prezi,
setLargeVideoVisible, ToolbarToggler, Util, VideoLayout */
var Etherpad = (function (my) {
var etherpadName = null;
var etherpadIFrame = null;
......@@ -41,18 +42,19 @@ var Etherpad = (function (my) {
largeVideo = $('#largeVideo');
if ($('#etherpad>iframe').css('visibility') === 'hidden') {
$('#activeSpeaker').css('visibility', 'hidden');
largeVideo.fadeOut(300, function () {
if (Prezi.isPresentationVisible()) {
largeVideo.css({opacity: '0'});
} else {
VideoLayout.setLargeVideoVisible(false);
}
});
$('#etherpad>iframe').fadeIn(300, function () {
document.body.style.background = '#eeeeee';
$('#etherpad>iframe').css({visibility: 'visible'});
$('#etherpad').css({zIndex: 2});
});
$('#etherpad>iframe').fadeIn(300, function () {
document.body.style.background = '#eeeeee';
$('#etherpad>iframe').css({visibility: 'visible'});
$('#etherpad').css({zIndex: 2});
});
}
else if ($('#etherpad>iframe')) {
......@@ -60,16 +62,22 @@ var Etherpad = (function (my) {
$('#etherpad>iframe').css({visibility: 'hidden'});
$('#etherpad').css({zIndex: 0});
document.body.style.background = 'black';
if (!isPresentation) {
$('#largeVideo').fadeIn(300, function () {
VideoLayout.setLargeVideoVisible(true);
});
}
});
if (!isPresentation) {
$('#largeVideo').fadeIn(300, function () {
VideoLayout.setLargeVideoVisible(true);
});
}
}
resize();
};
my.isVisible = function() {
var etherpadIframe = $('#etherpad>iframe');
return etherpadIframe && etherpadIframe.is(':visible');
};
/**
* Resizes the etherpad.
*/
......@@ -161,7 +169,7 @@ var Etherpad = (function (my) {
*/
$(document).bind('etherpadadded.muc', function (event, jid, etherpadName) {
console.log("Etherpad added", etherpadName);
if (config.etherpad_base && !focus) {
if (config.etherpad_base && !Moderator.isModerator()) {
Etherpad.init(etherpadName);
}
});
......@@ -169,6 +177,7 @@ var Etherpad = (function (my) {
/**
* On focus changed event.
*/
// FIXME: there is no such event as 'focusechanged.muc'
$(document).bind('focusechanged.muc', function (event, focus) {
console.log("Focus changed");
if (config.etherpad_base)
......
......@@ -29,8 +29,20 @@ config.page.configuration.record.disabled=Disabled
config.page.configuration.record.disabled_description=Audio and Video Recording disabled
config.page.configuration.allowdirectsip.enabled=Enabled
config.page.configuration.allowdirectsip.enabled_description=Allow Direct SIP enabled
config.page.configuration.allowdirectsip.disabled=Disbled
config.page.configuration.allowdirectsip.disabled=Disabled
config.page.configuration.allowdirectsip.disabled_description=Allow Direct SIP disabled
config.page.configuration.adaptivelastn.enabled=Enabled
config.page.configuration.adaptivelastn.enabled_description=Adaptive Last N enabled
config.page.configuration.adaptivelastn.disabled=Disabled
config.page.configuration.adaptivelastn.disabled_description=Adaptive Last N disabled
config.page.configuration.adaptivesimulcast.enabled=Enabled
config.page.configuration.adaptivesimulcast.enabled_description=Adaptive Simulcast enabled
config.page.configuration.adaptivesimulcast.disabled=Disabled
config.page.configuration.adaptivesimulcast.disabled_description=Adaptive Simulcast disabled
config.page.configuration.enablesimulcast.enabled=Enabled
config.page.configuration.enablesimulcast.enabled_description=Simulcast enabled
config.page.configuration.enablesimulcast.disabled=Disabled
config.page.configuration.enablesimulcast.disabled_description=Simulcast disabled
config.page.configuration.authusername=Username
config.page.configuration.sippassword=Password
config.page.configuration.server=Registration Server
......@@ -57,6 +69,7 @@ config.page.configuration.audiomixer.enabled_description=Audio Mixer enabled
config.page.configuration.audiomixer.disabled_description=Audio Mixer disabled
config.page.configuration.record.path=Recording Path
config.page.configuration.record.secret=Recording Password/Secret
config.page.configuration.advanced.features.title=Advanced Features
ofmeet.conference.summary=Below is an overview of meetings.
ofmeet.conference.expired=Conference has been expired.
ofmeet.summary.conferences=Total Conferences
......
......@@ -31,6 +31,7 @@
<script src="libs/rayo.js?v=1"></script>
<script src="libs/tooltip.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/pako.bundle.js?v=1"></script><!-- zlib deflate -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=4"></script>
<script src="muc.js?v=17"></script><!-- simple MUC library -->
......@@ -38,40 +39,42 @@
<script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
<script src="ofmeet-screenshare.js?v=1"></script><!-- BAO -->
<script src="data_channels.js?v=3"></script><!-- data channels -->
<script src="app.js?v=21"></script><!-- application logic -->
<script src="app.js?v=22"></script><!-- application logic -->
<script src="commands.js?v=1"></script><!-- application logic -->
<script src="chat.js?v=15"></script><!-- chat logic -->
<script src="contact_list.js?v=7"></script><!-- contact list logic -->
<script src="contact_list.js?v=8"></script><!-- contact list logic -->
<script src="side_panel_toggler.js?v=1"></script>
<script src="util.js?v=7"></script><!-- utility functions -->
<script src="etherpad.js?v=9"></script><!-- etherpad plugin -->
<script src="prezi.js?v=6"></script><!-- prezi plugin -->
<script src="etherpad.js?v=10"></script><!-- etherpad plugin -->
<script src="prezi.js?v=7"></script><!-- prezi plugin -->
<script src="smileys.js?v=3"></script><!-- smiley images -->
<script src="replacement.js?v=7"></script><!-- link and smiley replacement -->
<script src="moderatemuc.js?v=4"></script><!-- moderator plugin -->
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<script src="rtp_sts.js?v=5"></script><!-- RTP stats processing -->
<script src="local_sts.js?v=2"></script><!-- Local stats processing -->
<script src="videolayout.js?v=29"></script><!-- video ui -->
<script src="videolayout.js?v=31"></script><!-- video ui -->
<script src="connectionquality.js?v=1"></script>
<script src="toolbar.js?v=6"></script><!-- toolbar ui -->
<script src="toolbar_toggler.js?v=2"></script>
<script src="canvas_util.js?v=1"></script><!-- canvas drawing utils -->
<script src="audio_levels.js?v=2"></script><!-- audio levels plugin -->
<script src="audio_levels.js?v=4"></script><!-- audio levels plugin -->
<script src="media_stream.js?v=2"></script><!-- media stream -->
<script src="bottom_toolbar.js?v=6"></script><!-- media stream -->
<script src="moderator.js?v=2"></script><!-- media stream -->
<script src="roomname_generator.js?v=1"></script><!-- generator for random room names -->
<script src="keyboard_shortcut.js?v=3"></script>
<script src="recording.js?v=1"></script>
<script src="tracking.js?v=1"></script><!-- tracking -->
<script src="jitsipopover.js?v=3"></script>
<script src="message_handler.js?v=2"></script>
<script src="api_connector.js?v=2"></script>
<script src="settings_menu.js?v=1"></script>
<script src="avatar.js?v=1"></script><!-- avatars -->
<script src="avatar.js?v=4"></script><!-- avatars -->
<link rel="stylesheet" href="css/font.css?v=6"/>
<link rel="stylesheet" href="css/toastr.css?v=1">
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=30"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=14" id="videolayout_default"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=16" id="videolayout_default"/>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
<link rel="stylesheet" href="css/modaldialog.css?v=3">
......@@ -271,7 +274,10 @@
<a target="_new"><div class="watermark leftwatermark"></div></a>
<a target="_new"><div class="watermark rightwatermark"></div></a>
<a class="poweredby" href="http://jitsi.org" target="_new" >powered by jitsi.org</a>
<img id="activeSpeakerAvatar" src=""/>
<div id="activeSpeaker">
<img id="activeSpeakerAvatar" src=""/>
<canvas id="activeSpeakerAudioLevel"></canvas>
</div>
<video id="largeVideo" autoplay oncontextmenu="return false;"></video>
</div>
<div id="remoteVideos">
......
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.impl.protocol.jabber.*;
import net.java.sip.communicator.service.protocol.*;
import org.jitsi.protocol.xmpp.*;
import org.jivesoftware.smackx.muc.*;
/**
* Stripped Smack implementation of {@link ChatRoomMember}.
*
* @author Pawel Domas
*/
public class ChatMemberImpl
implements XmppChatMember
{
/**
* The MUC nickname used by this member.
*/
private final String nickname;
private final ChatRoomImpl chatRoom;
/**
* Full MUC address:
* room_name@muc.server.net/nickname
*/
private final String address;
/**
* Connection Jabber ID used to connect to the service. It is sent in
* MUC presence as jid attribute of item element:
*
* <x xmlns='http://jabber.org/protocol/muc#user'>
* <item affiliation='none'
* jid='hag66@shakespeare.lit/pda'
* role='participant'/>
* </x>
*
* In this example 'hag66@shakespeare.lit/pda' is the jabber ID.
* Note that by default only moderators are allowed to see it for
* all participants(see room configuration form and muc#roomconfig_whois
* for more details).
*/
private String jabberId;
private ChatRoomMemberRole role;
public ChatMemberImpl(String participant, ChatRoomImpl chatRoom)
{
this.address = participant;
this.nickname = participant.substring(participant.lastIndexOf("/")+1);
this.chatRoom = chatRoom;
}
@Override
public ChatRoom getChatRoom()
{
return chatRoom;
}
@Override
public ProtocolProviderService getProtocolProvider()
{
return chatRoom.getParentProvider();
}
@Override
public String getContactAddress()
{
return address;
}
@Override
public String getName()
{
return nickname;
}
@Override
public byte[] getAvatar()
{
return new byte[0];
}
@Override
public Contact getContact()
{
return null;
}
@Override
public ChatRoomMemberRole getRole()
{
if(this.role == null)
{
Occupant o = chatRoom.getOccupant(this);
if(o == null)
return ChatRoomMemberRole.GUEST;
else
this.role = ChatRoomJabberImpl
.smackRoleToScRole(o.getRole(), o.getAffiliation());
}
return this.role;
}
@Override
public void setRole(ChatRoomMemberRole role)
{
throw new RuntimeException("Not implemented yet.");
}
@Override
public String getJabberID()
{
return jabberId;
}
void setJabberID(String jabberId)
{
this.jabberId = jabberId;
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.util.*;
import org.jitsi.protocol.xmpp.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
/**
* Straightforward implementation of {@link OperationSetDirectSmackXmpp}
* for {@link org.jitsi.impl.protocol.xmpp.XmppProtocolProvider}.
*
* @author Pawel Domas
*/
public class OpSetDirectSmackXmppImpl
implements OperationSetDirectSmackXmpp
{
/**
* The logger used by this class.
*/
private final static Logger logger
= Logger.getLogger(OpSetDirectSmackXmppImpl.class);
/**
* Parent protocol provider service.
*/
private final XmppProtocolProvider xmppProvider;
/**
* Creates new instance of <tt>OpSetDirectSmackXmppImpl</tt>.
*
* @param xmppProvider parent {@link XmppProtocolProvider}.
*/
public OpSetDirectSmackXmppImpl(XmppProtocolProvider xmppProvider)
{
this.xmppProvider = xmppProvider;
}
/**
* {@inheritDoc}
*/
@Override
public XmppConnection getXmppConnection()
{
return xmppProvider.getConnectionAdapter();
}
/**
* {@inheritDoc}
*/
@Override
public void addPacketHandler(PacketListener listener, PacketFilter filter)
{
XMPPConnection connection = xmppProvider.getConnection();
if (connection != null)
{
connection.addPacketListener(listener, filter);
}
else
{
logger.error("Failed to add packet handler: "
+ listener + " - no valid connection object");
}
}
/**
* {@inheritDoc}
*/
@Override
public void removePacketHandler(PacketListener listener)
{
XMPPConnection connection = xmppProvider.getConnection();
if (connection != null)
{
xmppProvider.getConnection().removePacketListener(listener);
}
else
{
logger.error("Failed to remove packet handler: "
+ listener + " - no valid connection object");
}
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.util.*;
import org.jitsi.protocol.xmpp.*;
import org.jivesoftware.smack.*;
import java.util.*;
/**
*
*/
public class OpSetSimpleCapsImpl
implements OperationSetSimpleCaps
{
/**
* The logger.
*/
private final static Logger logger
= Logger.getLogger(OpSetSimpleCapsImpl.class);
private final XmppProtocolProvider xmppProvider;
public OpSetSimpleCapsImpl(XmppProtocolProvider xmppProtocolProvider)
{
this.xmppProvider = xmppProtocolProvider;
}
@Override
public List<String> getItems(String node)
{
try
{
return xmppProvider.discoverItems(node);
}
catch (XMPPException e)
{
logger.error("Error while discovering the services of " + node, e);
return null;
}
}
@Override
public boolean hasFeatureSupport(String node, String[] features)
{
return xmppProvider.checkFeatureSupport(
node, features);
}
//@Override
public boolean hasFeatureSupport(String node, String subnode,
String[] features)
{
return xmppProvider.checkFeatureSupport(node, subnode, features);
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.util.*;
import org.jitsi.jicofo.*;
import org.jitsi.protocol.xmpp.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smackx.pubsub.*;
import org.jivesoftware.smackx.pubsub.listener.*;
import java.util.*;
/**
* XMPP Pub-sub node implementation of {@link OperationSetSubscription}.
*
* @author Pawel Domas
*/
public class OpSetSubscriptionImpl
implements OperationSetSubscription,
ItemEventListener
{
/**
* The logger used by this instance.
*/
private final static Logger logger
= Logger.getLogger(OpSetSubscriptionImpl.class);
/**
* The configuration property used to specify address for pub-sub node.
*/
public static final String PUBSUB_ADDRESS_PNAME
= "org.jitsi.focus.pubsub.ADDRESS";
/**
* Smack PubSub manager.
*/
private PubSubManager manager;
/**
* PubSub address used.
*/
private final String pubSubAddress;
/**
* Our JID used for PubSub registration.
*/
private String ourJid;
/**
* The map of PubSub node listeners.
*/
private Map<String, SubscriptionListener> listenerMap
= new HashMap<String, SubscriptionListener>();
/**
* Parent XMPP provider used to hndle the protocol.
*/
private final XmppProtocolProvider parentProvider;
/**
* Creates new instance of {@link OpSetSubscriptionImpl}.
*
* @param parentProvider the XMPP provider instance.
*/
public OpSetSubscriptionImpl(XmppProtocolProvider parentProvider)
{
this.parentProvider = parentProvider;
this.pubSubAddress
= FocusBundleActivator.getConfigService()
.getString(PUBSUB_ADDRESS_PNAME);
}
/**
* Lazy initializer for our JID field(it's not available before provider
* gets connected).
*/
private String getOurJid()
{
if (ourJid == null)
{
this.ourJid = parentProvider.getOurJid();
}
return ourJid;
}
/**
* Lazy initializer for PubSub manager.
*/
private PubSubManager getManager()
{
if (manager == null)
{
manager = new PubSubManager(
parentProvider.getConnection(), pubSubAddress);
}
return manager;
}
/**
* Checks if given <tt>jid</tt> is registered for PubSub updates on given
* <tt>node</tt>.
*/
private boolean isSubscribed(String jid, Node node)
throws XMPPException
{
// FIXME: consider using local flag rather than getting the list
// of subscriptions
for (Subscription subscription : node.getSubscriptions())
{
if (subscription.getJid().equals(jid))
{
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void subscribe(String node, SubscriptionListener listener)
{
listenerMap.put(node, listener);
PubSubManager manager = getManager();
try
{
Node pubSubNode = manager.getNode(node);
if (!isSubscribed(getOurJid(), pubSubNode))
{
// FIXME: Is it possible that we will be subscribed after
// our connection dies ? If yes we won't add listener here
// and won't receive notifications
pubSubNode.addItemEventListener(this);
pubSubNode.subscribe(parentProvider.getOurJid());
}
}
catch (XMPPException e)
{
logger.error(e.getMessage(), e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void unSubscribe(String node)
{
if (!listenerMap.containsKey(node))
{
logger.warn("No PUBSUB listener for " + node);
return;
}
PubSubManager manager = getManager();
try
{
Node pubSubNode = manager.getNode(node);
if (isSubscribed(getOurJid(), pubSubNode))
{
pubSubNode.unsubscribe(getOurJid());
}
}
catch (XMPPException e)
{
logger.error(e.getMessage(), e);
}
}
/**
* Fired by Smack on PubSub update.
*
* {@inheritDoc}
*/
@Override
public void handlePublishedItems(ItemPublishEvent event)
{
String nodeId = event.getNodeId();
if (logger.isDebugEnabled())
logger.debug("PubSub update for node: " + nodeId);
SubscriptionListener listener = listenerMap.get(nodeId);
if (listener != null)
{
for(Object item : event.getItems())
{
if(!(item instanceof PayloadItem))
continue;
PayloadItem payloadItem = (PayloadItem) item;
listener.onSubscriptionUpdate(nodeId, payloadItem.getPayload());
}
}
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.util.*;
import org.jitsi.protocol.xmpp.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
/**
* Implementation of {@link OperationSetJingleImpl} for
* {@link XmppProtocolProvider}.
*
* @author Pawel Domas
*/
class OperationSetJingleImpl
extends AbstractOperationSetJingle
implements PacketFilter,
PacketListener
{
/**
* The logger used by this class.
*/
private final static Logger logger
= Logger.getLogger(OperationSetJingleImpl.class);
/**
* Parent {@link XmppProtocolProvider}.
*/
private final XmppProtocolProvider xmppProvider;
/**
* Creates new instance of <tt>OperationSetJingleImpl</tt>.
*
* @param xmppProvider parent XMPP protocol provider
*/
OperationSetJingleImpl(XmppProtocolProvider xmppProvider)
{
this.xmppProvider = xmppProvider;
}
/**
* Initializes this instance and binds packets processor.
*/
public void initialize()
{
xmppProvider.getConnection().addPacketListener(this, this);
}
/**
* Returns our XMPP address that will be used as 'from' attribute
* in Jingle QIs.
*/
protected String getOurJID()
{
return xmppProvider.getOurJid();
}
/**
* {@inheritDoc}
*/
protected XmppConnection getConnection()
{
return xmppProvider.getConnectionAdapter();
}
/**
* Packets filter implementation.
*
* {@inheritDoc}
*/
public boolean accept(Packet packet)
{
try
{
// We handle JingleIQ and SessionIQ.
if (!(packet instanceof JingleIQ))
{
// FIXME: find session for packet ID to make sure
// that the error belongs to this class
String packetID = packet.getPacketID();
XMPPError error = packet.getError();
if (error != null)
{
String errorMessage = error.getMessage();
logger.error(
"Received an error: code=" + error.getCode()
+ " message=" + errorMessage);
}
}
return packet instanceof JingleIQ
&& getSession(((JingleIQ) packet).getSID()) != null;
}
catch(Throwable t)
{
logger.error(t, t);
return false;
}
}
/**
* FIXME: this method can go to abstract class.
*
* {@inheritDoc}
*/
public void processPacket(Packet packet)
{
IQ iq = (IQ) packet;
//first ack all "set" requests.
if(iq.getType() == IQ.Type.SET)
{
IQ ack = IQ.createResultIQ(iq);
getConnection().sendPacket(ack);
}
try
{
if (iq instanceof JingleIQ)
processJingleIQ((JingleIQ) iq);
}
catch(Throwable t)
{
if (logger.isInfoEnabled())
{
String packetClass;
if (iq instanceof JingleIQ)
packetClass = "Jingle";
else
packetClass = packet.getClass().getSimpleName();
logger.info(
"Error while handling incoming " + packetClass
+ " packet: ",
t);
}
/*
* The Javadoc on ThreadDeath says: If ThreadDeath is caught by
* a method, it is important that it be rethrown so that the
* thread actually dies.
*/
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
}
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.service.protocol.*;
import org.jivesoftware.smack.packet.*;
/**
* Partial implementation of {@link OperationSetMeetToolsImpl}.
*
* @author Pawel Domas
*/
public class OperationSetMeetToolsImpl
implements OperationSetJitsiMeetTools
{
@Override
public void addSupportedFeature(String featureName)
{
}
@Override
public void sendPresenceExtension(ChatRoom chatRoom,
PacketExtension extension)
{
((ChatRoomImpl)chatRoom).sendPresenceExtension(extension);
}
@Override
public void setPresenceStatus(ChatRoom chatRoom, String statusMessage)
{
}
@Override
public void addRequestListener(
JitsiMeetRequestListener listener)
{
}
@Override
public void removeRequestListener(
JitsiMeetRequestListener listener)
{
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import java.util.*;
/**
* Multi user chat implementation stripped to the minimum required by the focus
* of Jitsi Meet conference. Uses Smack backend.
*
* @author Pawel Domas
*/
public class OperationSetMultiUserChatImpl
extends AbstractOperationSetMultiUserChat
{
/**
* The logger.
*/
private Logger logger = Logger.getLogger(OperationSetMultiUserChatImpl.class);
/**
* Parent protocol provider.
*/
private final XmppProtocolProvider protocolProvider;
/**
* The map of active chat rooms mapped by their names.
*/
private Map<String, ChatRoomImpl> rooms
= new HashMap<String, ChatRoomImpl>();
/**
* Creates new instance of {@link OperationSetMultiUserChatImpl}.
*
* @param protocolProvider parent protocol provider service.
*/
OperationSetMultiUserChatImpl(XmppProtocolProvider protocolProvider)
{
this.protocolProvider = protocolProvider;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getExistingChatRooms()
throws OperationFailedException, OperationNotSupportedException
{
return new ArrayList<String>(rooms.keySet());
}
/**
* {@inheritDoc}
*/
@Override
public List<ChatRoom> getCurrentlyJoinedChatRooms()
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getCurrentlyJoinedChatRooms(
ChatRoomMember chatRoomMember)
throws OperationFailedException, OperationNotSupportedException
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
public ChatRoom createChatRoom(String roomName,
Map<String, Object> roomProperties)
throws OperationFailedException, OperationNotSupportedException
{
if (rooms.containsKey(roomName))
{
throw new OperationFailedException(
"Room '" + roomName + "' exists",
OperationFailedException.GENERAL_ERROR);
}
ChatRoomImpl newRoom = new ChatRoomImpl(this, roomName);
rooms.put(roomName, newRoom);
return newRoom;
}
/**
* {@inheritDoc}
*/
@Override
public ChatRoom findRoom(String roomName)
throws OperationFailedException, OperationNotSupportedException
{
ChatRoom room = rooms.get(roomName);
if (room == null)
{
room = createChatRoom(roomName, null);
}
return room;
}
/**
* {@inheritDoc}
*/
@Override
public void rejectInvitation(ChatRoomInvitation invitation,
String rejectReason)
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMultiChatSupportedByContact(Contact contact)
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPrivateMessagingContact(String contactAddress)
{
throw new RuntimeException("Not implemented");
}
/**
* Returns Smack connection object used by parent protocol provider service.
*/
public Connection getConnection()
{
return protocolProvider.getConnection();
}
/**
* Returns parent protocol provider service.
*/
public XmppProtocolProvider getProtocolProvider()
{
return protocolProvider;
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.service.protocol.jabber.*;
import java.util.*;
/**
* Account ID used by Jitsi Meet focus XMPP accounts.
*
* FIXME: move code related to account properties initialization here,
* protocol provider or factory. Eventually remove this class.
*
* @author Pawel Domas
*/
public class XmppAccountID
extends JabberAccountID
{
public XmppAccountID(String id, Map<String, String> accountProperties)
{
super(id, accountProperties);
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.service.protocol.*;
import org.osgi.framework.*;
import java.util.*;
/**
* Bundle activator for {@link XmppProtocolProvider}.
*
* @author Pawel Domas
*/
public class XmppProtocolActivator
implements BundleActivator
{
private ServiceRegistration<?> focusRegistration;
@Override
public void start(BundleContext bundleContext)
throws Exception
{
XmppProviderFactory focusFactory
= new XmppProviderFactory(
bundleContext, ProtocolNames.JABBER);
Hashtable<String, String> hashtable = new Hashtable<String, String>();
// Register XMPP
hashtable.put(ProtocolProviderFactory.PROTOCOL,
ProtocolNames.JABBER);
focusRegistration = bundleContext.registerService(
ProtocolProviderFactory.class.getName(),
focusFactory,
hashtable);
}
@Override
public void stop(BundleContext bundleContext)
throws Exception
{
if (focusRegistration != null)
focusRegistration.unregister();
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp;
import net.java.sip.communicator.service.protocol.*;
import org.osgi.framework.*;
import java.util.*;
/**
* Protocol provider factory implementation ofr {@link XmppProtocolProvider}.
*
* @author Pawel Domas
*/
public class XmppProviderFactory
extends ProtocolProviderFactory
{
/**
* Creates a new <tt>ProtocolProviderFactory</tt>.
*
* @param bundleContext the bundle context reference of the service
* @param protocolName the name of the protocol
*/
protected XmppProviderFactory(
BundleContext bundleContext,
String protocolName)
{
super(bundleContext, protocolName);
}
/**
* {@inheritDoc}
*/
@Override
public AccountID installAccount(String s,
Map<String, String> stringStringMap)
throws IllegalArgumentException, IllegalStateException,
NullPointerException
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
public void modifyAccount(ProtocolProviderService protocolProviderService,
Map<String, String> stringStringMap)
throws NullPointerException
{
throw new RuntimeException("Not implemented");
}
/**
* {@inheritDoc}
*/
@Override
protected AccountID createAccountID(String userId,
Map<String, String> accountProperties)
{
return new XmppAccountID(userId, accountProperties);
}
/**
* {@inheritDoc}
*/
@Override
protected ProtocolProviderService createService(String userID,
AccountID accountID)
{
return new XmppProtocolProvider(accountID);
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp.extensions;
import org.jitsi.util.*;
import org.jivesoftware.smack.packet.*;
/**
* IQ used for the signaling of audio muting functionality in Jitsi Meet
* conferences.
*
* @author Pawel Domas
*/
public class MuteIq
extends IQ
{
/**
* Name space of mute packet extension.
*/
public static final String NAMESPACE = "http://jitsi.org/jitmeet/audio";
/**
* XML element name of mute packet extension.
*/
public static final String ELEMENT_NAME = "mute";
/**
* Attribute name of "jid".
*/
public static final String JID_ATTR_NAME = "jid";
/**
* Muted peer MUC jid.
*/
private String jid;
/**
* To mute or unmute.
*/
private Boolean mute;
@Override
public String getChildElementXML()
{
StringBuilder output = new StringBuilder();
output.append("<mute ")
.append("xmlns='").append(NAMESPACE).append("' ");
if (!StringUtils.isNullOrEmpty(jid))
{
output.append("jid='").append(jid).append("'");
}
if (mute != null)
{
output.append(">").append(mute).append("</mute>");
}
else
{
output.append("/>");
}
return output.toString();
}
/**
* Sets the MUC jid of the user to be muted/unmuted.
* @param jid muc jid in the form of room_name@muc.server.net/nickname.
*/
public void setJid(String jid)
{
this.jid = jid;
}
/**
* Returns MUC jid of the participant in the form of
* "room_name@muc.server.net/nickname".
*/
public String getJid()
{
return jid;
}
/**
* The action contained in the text part of 'mute' XML element body.
* @param mute <tt>true</tt> to mute the participant. <tt>null</tt> means no
* action is included in result XML.
*/
public void setMute(Boolean mute)
{
this.mute = mute;
}
/**
* Returns <tt>true</tt> to mute the participant, <tt>false</tt> to unmute
* or <tt>null</tt> if the action has not been specified(which is invalid).
*/
public Boolean getMute()
{
return mute;
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp.extensions;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
/**
* The parser of {@link MuteIq}.
*
* @author Pawel Domas
*/
public class MuteIqProvider
implements IQProvider
{
/**
* Registers this IQ provider into given <tt>ProviderManager</tt>.
* @param providerManager the <tt>ProviderManager</tt> to which this
* instance wil be bound to.
*/
public void registerMuteIqProvider(ProviderManager providerManager)
{
providerManager.addIQProvider(
MuteIq.ELEMENT_NAME,
MuteIq.NAMESPACE,
this);
}
/**
* {@inheritDoc}
*/
@Override
public IQ parseIQ(XmlPullParser parser)
throws Exception
{
String namespace = parser.getNamespace();
// Check the namespace
if (!MuteIq.NAMESPACE.equals(namespace))
{
return null;
}
String rootElement = parser.getName();
MuteIq iq;
if (MuteIq.ELEMENT_NAME.equals(rootElement))
{
iq = new MuteIq();
String jid
= parser.getAttributeValue("", MuteIq.JID_ATTR_NAME);
iq.setJid(jid);
}
else
{
return null;
}
boolean done = false;
while (!done)
{
switch (parser.next())
{
case XmlPullParser.END_TAG:
{
String name = parser.getName();
if (rootElement.equals(name))
{
done = true;
}
break;
}
case XmlPullParser.TEXT:
{
Boolean mute = Boolean.parseBoolean(parser.getText());
iq.setMute(mute);
break;
}
}
}
return iq;
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.protocol.xmpp.extensions;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
/**
* @author George Politis
*/
public class UserAgentPacketExtension
implements PacketExtension
{
/**
* Name space of browser packet extension.
*/
public static final String NAMESPACE = "http://jitsi.org/jitmeet/user-agent";
/**
* XML element name of browser packet extension.
*/
public static final String ELEMENT_NAME = "user-agent";
/**
* The browser name.
*/
private String userAgent = null;
/**
* Ctor.
*
* @param userAgent
*/
public UserAgentPacketExtension(String userAgent)
{
this.userAgent = userAgent;
}
/**
* Gets the user agent.
*
* @return the user agent.
*/
public String getUserAgent()
{
return this.userAgent;
}
/**
* Sets the user agent.
*
* @param userAgent the user agent.
*/
public void setUserAgent(String userAgent)
{
this.userAgent = userAgent;
}
public String getElementName()
{
return ELEMENT_NAME;
}
public String getNamespace()
{
return NAMESPACE;
}
public String toXML()
{
return new StringBuilder()
.append("<").append(ELEMENT_NAME).append(" xmlns=\"")
.append(NAMESPACE)
.append("\">")
.append(this.getUserAgent())
.append("</")
.append(ELEMENT_NAME)
.append('>')
.toString();
}
public static class Provider implements PacketExtensionProvider
{
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception
{
parser.next();
String userAgent = parser.getText();
while (parser.getEventType() != XmlPullParser.END_TAG)
{
parser.next();
}
return new UserAgentPacketExtension(userAgent);
}
}
}
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.jicofo;
import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.service.protocol.*;
import org.jitsi.protocol.xmpp.*;
/**
* Class adds utility methods that require use of package protected methods of
* {@link JitsiMeetConference}. These are used only for test purposes, so are
* placed in separate class to reduce size of the conference focus class.
*
* @author Pawel Domas
*/
public class ConferenceUtility
{
/**
* Conference instance.
*/
private final JitsiMeetConference conference;
/**
* Creates new instance for given <tt>JitsiMeetConference</tt>.
* @param conference the conference that wil be used by this instance.
*/
public ConferenceUtility(JitsiMeetConference conference)
{
this.conference = conference;
}
/**
* Returns the ID of Colibri conference hosted on the videobridge.
*/
public String getJvbConferenceId()
{
ProtocolProviderService pps = conference.getXmppProvider();
OperationSetColibriConference colibri
= pps.getOperationSet(OperationSetColibriConference.class);
return colibri.getConferenceId();
}
/**
* Returns the id of video channel allocated for the participant with given
* JID.
* @param participantJid the MUC JID of the participant for whom we want to
* get video channel id.
*/
public String getParticipantVideoChannelId(String participantJid)
{
Participant participant
= conference.findParticipantForRoomJid(participantJid);
ColibriConferenceIQ channelsInfo
= participant.getColibriChannelsInfo();
ColibriConferenceIQ.Content videoContent
= channelsInfo.getContent("video");
ColibriConferenceIQ.Channel videoChannel
= videoContent.getChannel(0);
return videoChannel.getID();
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.jicofo.log;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.XmlPullParser;
/**
* Implements a <tt>PacketExtensionProvider</tt> for XEP-0337 log extensions.
*
* @author Boris Grozev
*/
public class LogExtensionProvider
implements PacketExtensionProvider
{
/**
* {@inheritDoc}
*/
@Override
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception
{
LogPacketExtension result = new LogPacketExtension();
for (int i = 0; i < parser.getAttributeCount(); i++)
{
result.setAttribute(
parser.getAttributeName(i),
parser.getAttributeValue(i));
}
boolean done = false;
int eventType;
String elementName;
while (!done)
{
eventType = parser.next();
elementName = parser.getName();
if (eventType == XmlPullParser.START_TAG)
{
if (LogPacketExtension.MESSAGE_ELEM_NAME.equals(elementName))
{
result.setMessage(parser.nextText());
}
else if(LogPacketExtension.TAG_ELEM_NAME.equals(elementName))
{
String nameAttr = null;
String valueAttr = null;
for (int i = 0; i < parser.getAttributeCount(); i++)
{
String attrName = parser.getAttributeName(i);
if ("name".equals(attrName))
nameAttr = parser.getAttributeValue(i);
else if ("value".equals(attrName))
valueAttr = parser.getAttributeValue(i);
}
if (nameAttr != null && valueAttr != null)
{
result.addTag(nameAttr, valueAttr);
}
}
}
else if (eventType == XmlPullParser.END_TAG)
{
if (LogPacketExtension.LOG_ELEM_NAME.equals(elementName))
done = true;
}
}
return result;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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