diff --git a/bbb-api-demo/src/main/webapp/bbb_api.jsp b/bbb-api-demo/src/main/webapp/bbb_api.jsp index 1bd5dbc2833b..4ec2debaac92 100755 --- a/bbb-api-demo/src/main/webapp/bbb_api.jsp +++ b/bbb-api-demo/src/main/webapp/bbb_api.jsp @@ -296,8 +296,17 @@ public String getJoinURLwithDynamicConfigXML(String username, String meetingID, xml_param = xml_param.replace("> <", "><"); } - String setConfigXML_parameters = "meetingID=" + urlEncode(meetingID) + - "&checksum=" + checksum(meetingID + encodeURIComponent(xml_param) + salt) +"&configXML=" + urlEncode(encodeURIComponent(xml_param)); + /** Create the parameters we want to send to the server. **/ + Map paramsMap = new HashMap(); + paramsMap.put("meetingID", new String[]{urlEncode(meetingID)}); + paramsMap.put("configXML", new String[]{urlEncode(xml_param)}); + + String baseString = createBaseString(paramsMap); + String checksumString = createChecksum("setConfigXML", baseString); + + System.out.println("Base String = [" + baseString + "]"); + + String setConfigXML_parameters = baseString + "&checksum=" + checksumString; url = ""; try { @@ -322,15 +331,46 @@ public String getJoinURLwithDynamicConfigXML(String username, String meetingID, // // And finally return a URL to join that meeting // - String join_parameters = "meetingID=" + urlEncode(meetingID) - + "&fullName=" + urlEncode(username) + "&password=mp&configToken=" + configToken; - - return base_url_join + join_parameters + "&checksum=" - + checksum("join" + join_parameters + salt); + String join_parameters = "meetingID=" + urlEncode(meetingID) + "&fullName=" + urlEncode(username) + "&password=mp&configToken=" + configToken; + + return base_url_join + join_parameters + "&checksum=" + checksum("join" + join_parameters + salt); + +} + +// From the list of parameters we want to pass. Creates a base string with parameters +// sorted in alphabetical order for us to sign. +public String createBaseString(Map params) { + StringBuffer csbuf = new StringBuffer(); + SortedSet keys = new TreeSet(params.keySet()); + + boolean first = true; + String checksum = null; + for (String key: keys) { + for (String value: params.get(key)) { + if (first) { + first = false; + } else { + csbuf.append("&"); + } + csbuf.append(key); + csbuf.append("="); + csbuf.append(value); + } + } -} + return csbuf.toString(); + } + // Get a checksum for our basestring. + public String createChecksum(String apiCall, String baseString) { + StringBuffer csbuf = new StringBuffer(); + csbuf.append(apiCall); + csbuf.append(baseString); + csbuf.append(salt); + //System.out.println("Calc checksum for =[" + csbuf.toString() + "]"); + return DigestUtils.shaHex(csbuf.toString()); + } // //Create a meeting and return a URL to join it as moderator diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties index 8c0609a0569c..e3a42a12cc8f 100755 --- a/bigbluebutton-client/locale/en_US/bbbResources.properties +++ b/bigbluebutton-client/locale/en_US/bbbResources.properties @@ -332,7 +332,7 @@ bbb.shortcutkey.share.webcam = 66 bbb.shortcutkey.share.webcam.function = Open webcam sharing window bbb.shortcutkey.shortcutWindow = 72 -bbb.shortcutkey.shortcutWindow.function = Open shortcut help window +bbb.shortcutkey.shortcutWindow.function = Open/focus to shortcut help window bbb.shortcutkey.logout = 76 bbb.shortcutkey.logout.function = Log out of this meeting bbb.shortcutkey.raiseHand = 82 @@ -359,12 +359,14 @@ bbb.shortcutkey.users.mute = 83 bbb.shortcutkey.users.mute.function = Mute or Unmute selected person bbb.shortcutkey.users.muteall = 65 bbb.shortcutkey.users.muteall.function = Mute or Unmute all users -bbb.shortcutkey.users.focusUsers = 70 +bbb.shortcutkey.users.focusUsers = 85 bbb.shortcutkey.users.focusUsers.function = Focus to users list +bbb.shortcutkey.users.muteAllButPres = 65 +bbb.shortcutkey.users.muteAllButPres.function = Mute everyone but the Presenter bbb.shortcutkey.chat.focusTabs = 89 bbb.shortcutkey.chat.focusTabs.function = Focus to chat tabs -bbb.shortcutkey.chat.focusBox = 86 +bbb.shortcutkey.chat.focusBox = 66 bbb.shortcutkey.chat.focusBox.function = Focus to chat box bbb.shortcutkey.chat.changeColour = 67 bbb.shortcutkey.chat.changeColour.function = Focus to font colour picker. @@ -377,7 +379,7 @@ bbb.shortcutkey.chat.chatbox.advance = 32 bbb.shortcutkey.chat.chatbox.advance.function = Navigate to the next message bbb.shortcutkey.chat.chatbox.goback = 80 bbb.shortcutkey.chat.chatbox.goback.function = Navigate to the previous message -bbb.shortcutkey.chat.chatbox.repeat = 85 +bbb.shortcutkey.chat.chatbox.repeat = 82 bbb.shortcutkey.chat.chatbox.repeat.function = Repeat current message bbb.shortcutkey.chat.chatbox.golatest = 89 bbb.shortcutkey.chat.chatbox.golatest.function = Navigate to the latest message diff --git a/bigbluebutton-client/resources/prod/lib/bbb_blinker.js b/bigbluebutton-client/resources/prod/lib/bbb_blinker.js index d0cd7e66c511..029ec286e90c 100755 --- a/bigbluebutton-client/resources/prod/lib/bbb_blinker.js +++ b/bigbluebutton-client/resources/prod/lib/bbb_blinker.js @@ -56,13 +56,35 @@ function determineModifier() modifier = "control+"; } else if (browser == "Microsoft Internet Explorer"){ - modifier = "shift+alt+"; + modifier = "control+shift+"; } - /*else if (browser == "Safari"){ - modifier = "control+"; - }*/ + //else if (browser == "Safari"){ + // modifier = "control+shift+"; + //} + else{ + modifier = "control+shift+"; + } + return modifier; +} + +function determineGlobalModifier() +{ + var browser = determineBrowser(); + var modifier; + if (browser == "Firefox"){ + modifier = "control+shift+"; + } + else if (browser == "Chrome"){ + modifier = "control+shift+"; + } + else if (browser == "Microsoft Internet Explorer"){ + modifier = "control+alt+"; + } + //else if (browser == "Safari"){ + // modifier = "control+alt"; + //} else{ - modifier = "shift+alt+"; + modifier = "control+alt+"; } return modifier; } diff --git a/bigbluebutton-client/src/BigBlueButton.mxml b/bigbluebutton-client/src/BigBlueButton.mxml index 9b0d490d1635..998168faed75 100755 --- a/bigbluebutton-client/src/BigBlueButton.mxml +++ b/bigbluebutton-client/src/BigBlueButton.mxml @@ -49,7 +49,7 @@ with BigBlueButton; if not, see . import org.bigbluebutton.util.i18n.ResourceUtil; private var langResources:ResourceUtil = null; //ResourceUtil.getInstance(); - private var modifier:String; + private var globalModifier:String; /** * Thse two lines are workaround for this. (ralam - Nov 8, 2008) * http://gregjessup.com/flex-3-advanceddatagrid-cannot-convert-mxmanagersdragmanagerimpl-to-mxmanagersidragmanager/ @@ -67,7 +67,7 @@ with BigBlueButton; if not, see . setupAPI(); EventBroadcaster.getInstance().addEventListener("configLoadedEvent", configLoadedEventHandler); BBB.initConfigManager(); - modifier = ExternalInterface.call("determineModifier"); + globalModifier = ExternalInterface.call("determineGlobalModifier"); } private function configLoadedEventHandler(e:Event):void { @@ -104,31 +104,32 @@ with BigBlueButton; if not, see . */ private function localeChanged(e:Event):void{ - loadKeyCombos(modifier); + loadKeyCombos(globalModifier); } private var keyCombos:Object; - private function loadKeyCombos(modifier:String):void { + private function loadKeyCombos(globalModifier:String):void { keyCombos = new Object(); // always start with a fresh array - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.flash.exit') as String)] = ShortcutEvent.FOCUS_AWAY_EVENT; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.users.muteme') as String)] = ShortcutEvent.MUTE_ME_EVENT; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.chat.chatinput') as String)] = ShortcutEvent.FOCUS_CHAT_INPUT; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.flash.exit') as String)] = ShortcutEvent.FOCUS_AWAY_EVENT; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.users.muteme') as String)] = ShortcutEvent.MUTE_ME_EVENT; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.chat.chatinput') as String)] = ShortcutEvent.FOCUS_CHAT_INPUT; // General hotKeys (usable from anywhere in the application) - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.users') as String)] = ShortcutEvent.FOCUS_USERS_WINDOW; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.video') as String)] = ShortcutEvent.FOCUS_VIDEO_WINDOW; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.presentation') as String)] = ShortcutEvent.FOCUS_PRESENTATION_WINDOW; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.chat') as String)] = ShortcutEvent.FOCUS_CHAT_WINDOW; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.users') as String)] = ShortcutEvent.FOCUS_USERS_WINDOW; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.video') as String)] = ShortcutEvent.FOCUS_VIDEO_WINDOW; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.presentation') as String)] = ShortcutEvent.FOCUS_PRESENTATION_WINDOW; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.focus.chat') as String)] = ShortcutEvent.FOCUS_CHAT_WINDOW; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.desktop') as String)] = ShortcutEvent.SHARE_DESKTOP; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.webcam') as String)] = ShortcutEvent.SHARE_WEBCAM; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.microphone') as String)] = ShortcutEvent.SHARE_MICROPHONE; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.desktop') as String)] = ShortcutEvent.SHARE_DESKTOP; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.webcam') as String)] = ShortcutEvent.SHARE_WEBCAM; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.microphone') as String)] = ShortcutEvent.SHARE_MICROPHONE; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.shortcutWindow') as String)] = ShortcutEvent.REMOTE_OPEN_SHORTCUT_WIN; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.logout') as String)] = ShortcutEvent.LOGOUT; - keyCombos[modifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.raiseHand') as String)] = ShortcutEvent.RAISE_HAND; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.shortcutWindow') as String)] = ShortcutEvent.REMOTE_OPEN_SHORTCUT_WIN; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.logout') as String)] = ShortcutEvent.LOGOUT; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.raiseHand') as String)] = ShortcutEvent.RAISE_HAND; + keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.users.muteAllButPres') as String)] = ShortcutEvent.MUTE_ALL_BUT_PRES; } public function hotkeyCapture():void{ @@ -140,7 +141,7 @@ with BigBlueButton; if not, see . // Handle general-access hotkeys, regardless of what window the user is focused in private function handleKeyDown(e:KeyboardEvent) :void { - if (keyCombos == null) loadKeyCombos(modifier); + if (keyCombos == null) loadKeyCombos(globalModifier); var keyPress:String = (e.ctrlKey ? "control+" : "") + (e.shiftKey ? "shift+" : "") + (e.altKey ? "alt+" : "") + e.keyCode; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as index 1ca33d13e446..f5a948e8eae5 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as @@ -71,6 +71,8 @@ package org.bigbluebutton.main.events { public static const CHANGE_FONT_COLOUR:String = 'CHANGE_FONT_COLOUR'; public static const SEND_MESSAGE:String = 'SEND_MESSAGE'; + public static const MUTE_ALL_BUT_PRES:String = 'MUTE_ALL_BUT_PRES'; + // Temporary string to help fix chat message navigation for screen readers public static const CHAT_DEBUG:String = 'CHAT_DEBUG'; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as index 14fe8f05f700..adf9961018cd 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as @@ -103,6 +103,8 @@ package org.bigbluebutton.main.model.modules private function stopModule(name:String):void { LogUtil.debug('Stopping module ' + name); + trace('Stopping module ' + name); + var m:ModuleDescriptor = getModule(name); if (m != null) { LogUtil.debug('Stopping ' + name); diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as index 7294f13c1454..98cf3434a3f6 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as @@ -81,9 +81,9 @@ package org.bigbluebutton.main.model.users { * is the same then use userID which should be unique making the order the same * across all clients. */ - if (a.name.toLowerCase() > b.name.toLowerCase()) + if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; - else if (a.name.toLowerCase() < b.name.toLowerCase()) + else if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; else if (a.userID.toLowerCase() > b.userID.toLowerCase()) return -1; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml index d577a0cfba37..9a5f2bb36ff9 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml @@ -246,7 +246,8 @@ with BigBlueButton; if not, see . toolTip="{ResourceUtil.getInstance().getString('bbb.mainToolbar.shortcutBtn.toolTip')}" tabIndex="{baseIndex+numButtons+6}" /> + styleName="helpLinkButtonStyle" tabIndex="{baseIndex+numButtons+7}" + toolTip="{ResourceUtil.getInstance().getString('bbb.mainToolbar.helpBtn')}"/> diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml index 64a5aff4626e..9c1d3ab9db27 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml @@ -23,6 +23,7 @@ with BigBlueButton; if not, see . @@ -33,6 +34,8 @@ with BigBlueButton; if not, see . . private var chatKeys:ArrayList; private var userKeys:ArrayList; - private var genResource:Array = ['bbb.shortcutkey.general.minimize', 'bbb.shortcutkey.general.maximize', - 'bbb.shortcutkey.flash.exit', 'bbb.shortcutkey.focus.users', 'bbb.shortcutkey.focus.video', - 'bbb.shortcutkey.focus.presentation', 'bbb.shortcutkey.focus.chat', 'bbb.shortcutkey.share.desktop', + private var genResource:Array = ['bbb.shortcutkey.general.minimize', 'bbb.shortcutkey.general.maximize', 'bbb.shortcutkey.flash.exit', + 'bbb.shortcutkey.focus.users', 'bbb.shortcutkey.focus.video', 'bbb.shortcutkey.focus.presentation', + 'bbb.shortcutkey.focus.chat', 'bbb.shortcutkey.chat.chatinput', 'bbb.shortcutkey.share.desktop', 'bbb.shortcutkey.share.microphone', 'bbb.shortcutkey.share.webcam', 'bbb.shortcutkey.shortcutWindow', - 'bbb.shortcutkey.logout', 'bbb.shortcutkey.raiseHand', 'bbb.shortcutkey.users.muteme']; + 'bbb.shortcutkey.logout', 'bbb.shortcutkey.raiseHand', 'bbb.shortcutkey.users.muteme', + 'bbb.shortcutkey.users.muteAllButPres']; - private var presResource:Array = ['bbb.shortcutkey.focus.presentation', 'bbb.shortcutkey.present.focusslide', 'bbb.shortcutkey.whiteboard.undo', - 'bbb.shortcutkey.present.upload', 'bbb.shortcutkey.present.previous', 'bbb.shortcutkey.present.select', - 'bbb.shortcutkey.present.next', 'bbb.shortcutkey.present.fitWidth', 'bbb.shortcutkey.present.fitPage']; + private var presResource:Array = ['bbb.shortcutkey.present.focusslide', /*'bbb.shortcutkey.whiteboard.undo',*/ 'bbb.shortcutkey.present.upload', + 'bbb.shortcutkey.present.previous', 'bbb.shortcutkey.present.select', 'bbb.shortcutkey.present.next', + 'bbb.shortcutkey.present.fitWidth', 'bbb.shortcutkey.present.fitPage']; - private var chatResource:Array = ['bbb.shortcutkey.focus.chat', 'bbb.shortcutkey.chat.chatinput', 'bbb.shortcutkey.chat.focusTabs', - 'bbb.shortcutkey.chat.focusBox', 'bbb.shortcutkey.chat.changeColour', 'bbb.shortcutkey.chat.sendMessage', - 'bbb.shortcutkey.chat.explanation', - 'bbb.shortcutkey.chat.chatbox.gofirst', 'bbb.shortcutkey.chat.chatbox.goback', 'bbb.shortcutkey.chat.chatbox.repeat', - 'bbb.shortcutkey.chat.chatbox.advance', 'bbb.shortcutkey.chat.chatbox.golatest', 'bbb.shortcutkey.chat.chatbox.goread']; + private var chatResource:Array = ['bbb.shortcutkey.chat.focusTabs', 'bbb.shortcutkey.chat.focusBox', /*'bbb.shortcutkey.chat.changeColour',*/ + 'bbb.shortcutkey.chat.sendMessage', 'bbb.shortcutkey.chat.explanation', 'bbb.shortcutkey.chat.chatbox.gofirst', + 'bbb.shortcutkey.chat.chatbox.goback', 'bbb.shortcutkey.chat.chatbox.repeat', 'bbb.shortcutkey.chat.chatbox.advance', + 'bbb.shortcutkey.chat.chatbox.golatest', 'bbb.shortcutkey.chat.chatbox.goread']; - private var userResource:Array = ['bbb.shortcutkey.focus.users', 'bbb.shortcutkey.users.focusUsers', 'bbb.shortcutkey.users.makePresenter', 'bbb.shortcutkey.users.mute', - 'bbb.shortcutkey.users.kick', 'bbb.shortcutkey.users.muteall']; + private var userResource:Array = ['bbb.shortcutkey.users.focusUsers', 'bbb.shortcutkey.users.makePresenter', 'bbb.shortcutkey.users.mute', + /*'bbb.shortcutkey.users.kick',*/ 'bbb.shortcutkey.users.muteall']; [Bindable] private var shownKeys:ArrayCollection; @@ -71,8 +74,12 @@ with BigBlueButton; if not, see . [Bindable] private var baseIndex:int = 100; + private var modifier:String; + private var globalModifier:String; + private function init():void { - + modifier = ExternalInterface.call("determineModifier"); + globalModifier = ExternalInterface.call("determineGlobalModifier"); } private function onCreationComplete():void { @@ -86,7 +93,7 @@ with BigBlueButton; if not, see . } private function reloadKeys():void { - genKeys = loadKeys(genResource); + genKeys = loadKeys(genResource, true); presKeys = loadKeys(presResource); chatKeys = loadKeys(chatResource); userKeys = loadKeys(userResource); @@ -112,14 +119,17 @@ with BigBlueButton; if not, see . } } - private function loadKeys(resource:Array):ArrayList { + private function loadKeys(resource:Array, global:Boolean=false):ArrayList { var keyList:ArrayList = new ArrayList(); var keyCombo:String; - var modifier:String; + var mod:String; + + if (global) + mod = globalModifier; + else + mod = modifier; + for (var i:int = 0; i < resource.length; i++) { - - // Find the modifier key(s) for the user's browser - modifier = ExternalInterface.call("determineModifier"); keyCombo = ResourceUtil.getInstance().getString(resource[i]); var key:int = int(keyCombo); var convKey:String; @@ -143,7 +153,7 @@ with BigBlueButton; if not, see . if (keyCombo == "----"){ keyList.addItem({shortcut:(ResourceUtil.getInstance().getString(resource[i] + '.function')), func:""}); } else{ - keyList.addItem({shortcut:modifier + convKey, func:(ResourceUtil.getInstance().getString(resource[i] + '.function'))}); + keyList.addItem({shortcut:mod + convKey, func:(ResourceUtil.getInstance().getString(resource[i] + '.function'))}); } } return keyList; diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatView.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatView.mxml index de8b7140f6f0..8fed71bd0821 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatView.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatView.mxml @@ -45,26 +45,26 @@ with BigBlueButton; if not, see . import flash.media.Sound; import flash.media.SoundChannel; import flash.utils.Timer; - + import flexlib.controls.tabBarClasses.SuperTab; import flexlib.controls.textClasses.StringBoundaries; import flexlib.events.SuperTabEvent; import flexlib.mdi.containers.MDIWindow; - + import mx.collections.ArrayCollection; import mx.containers.ControlBar; import mx.controls.Button; import mx.core.Container; import mx.core.UIComponent; import mx.events.IndexChangedEvent; - + import org.bigbluebutton.common.LogUtil; import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.EventConstants; import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.events.CoreEvent; import org.bigbluebutton.core.managers.UserManager; - import org.bigbluebutton.main.events.BBBEvent; + import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.events.ShortcutEvent; import org.bigbluebutton.main.model.users.BBBUser; import org.bigbluebutton.modules.chat.ChatUtil; @@ -266,7 +266,11 @@ with BigBlueButton; if not, see . chatBox.id = chatWithUserID; chatBox.name = chatWithUserID; chatBox.baseIndex = baseIndex; - + + if (chatTabs.getChildByName(TAB_BOX_ID)) { + chatBox.setStyle("fontSize", int((chatTabs.getChildByName(TAB_BOX_ID) as AddChatTabBox).cmbFontSize.selectedItem)); + } + if (publicChat) { chatBox.label = PUBLIC_CHAT_USERNAME chatBox.publicChat = true; diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml index 0e1b3500dd4c..973e83ec223d 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml @@ -33,6 +33,7 @@ + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as index 487c6b4ce8ae..8153cdb5ce44 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as @@ -162,9 +162,9 @@ package org.bigbluebutton.modules.videoconf.business ns.publish(e.stream); } - - public function stopBroadcasting():void{ + trace("Closing netstream for webcam publishing"); + if (ns != null) { ns.attachCamera(null); ns.close(); @@ -174,6 +174,8 @@ package org.bigbluebutton.modules.videoconf.business } public function disconnect():void { + trace("VideoProxy:: disconnecting from Video application"); + stopBroadcasting(); if (nc != null) nc.close(); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as index 633a0c0c7ac1..ca920fd421a2 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as @@ -63,9 +63,7 @@ package org.bigbluebutton.modules.videoconf.business protected var _minHeight:int = 120 + PADDING_VERTICAL; protected var aspectRatio:Number = 1; protected var keepAspect:Boolean = false; -// protected var originalWidth:Number; -// protected var originalHeight:Number; - + protected var mousePositionOnDragStart:Point; public var streamName:String; @@ -213,6 +211,8 @@ package org.bigbluebutton.modules.videoconf.business } override public function close(event:MouseEvent = null):void{ + trace("VideoWIndowItf close window event"); + var e:CloseWindowEvent = new CloseWindowEvent(); e.window = this; dispatchEvent(e); diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml index 0090e6109c7c..d9de68f4da3e 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml @@ -47,7 +47,7 @@ with BigBlueButton; if not, see . - + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as index 77ba1f4ce095..9f78cf4f0dcb 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as @@ -85,11 +85,7 @@ package org.bigbluebutton.modules.videoconf.maps trace("VideoEventMapDelegate:: [" + me + "] Video Module Started."); this.uri = uri; } - - public function stop():void { - - } - + public function viewCamera(userID:String, stream:String, name:String, mock:Boolean = false):void { trace("VideoEventMapDelegate:: [" + me + "] viewCamera. ready = [" + _ready + "]"); @@ -293,6 +289,8 @@ package org.bigbluebutton.modules.videoconf.maps } private function stopBroadcasting():void { + trace("Stopping broadcast of webcam"); + proxy.stopBroadcasting(); _isPublishing = false; @@ -344,11 +342,13 @@ package org.bigbluebutton.modules.videoconf.maps } public function stopModule():void { + trace("VideoEventMapDelegate:: stopping video module"); closeAllWindows(); proxy.disconnect(); } public function closeAllWindows():void{ + trace("VideoEventMapDelegate:: closing all windows"); if (_isPublishing) { stopBroadcasting(); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/AvatarWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/AvatarWindow.mxml index 25510736574d..0764e0b03296 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/AvatarWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/AvatarWindow.mxml @@ -211,8 +211,6 @@ with BigBlueButton; if not, see . } private function startPublishing():void{ - - maximizeRestoreBtn.visible = true; this.resizable = true; onResize(); diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml index 53be9876bd68..a0a3a1093cf8 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml @@ -140,10 +140,6 @@ with BigBlueButton; if not, see . titleBarOverlay.tabIndex = baseIndex; titleBarOverlay.accessibilityName = ResourceUtil.getInstance().getString('bbb.video.publish.titleBar'); - minimizeBtn.tabIndex = baseIndex+1; - minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString('bbb.video.publish.minimizeBtn'); - maximizeRestoreBtn.tabIndex = baseIndex+2; - maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString('bbb.video.publish.maximizeRestoreBtn'); closeBtn.focusEnabled = true; closeBtn.tabIndex = baseIndex+3; @@ -311,8 +307,6 @@ with BigBlueButton; if not, see . e.camera = _camera; dispatchEvent(e); - maximizeRestoreBtn.visible = true; - // Store the userid for the publisher. This allows us to control // the user's status from the video window userID = UsersUtil.getMyUserID(); @@ -325,7 +319,8 @@ with BigBlueButton; if not, see . private var _isClosing:Boolean = false; override public function close(event:MouseEvent=null):void{ - trace("PublishWindow::close"); + trace("PublishWindow:: closing"); + if (!_isClosing) { _isClosing = true; stopPublishing(); @@ -333,9 +328,13 @@ with BigBlueButton; if not, see . } private function stopCamera():void { + trace("PublishWindow:: stopping camera"); + _camera = null; if (_video != null) { + trace("PublishWindow:: removing video from display"); _videoHolder.removeChild(_video); + _video.attachCamera(null); _video.clear(); _video = null; diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml index 94175b8e18c0..cac51698c109 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml @@ -102,7 +102,7 @@ with BigBlueButton; if not, see . this.minWidth = _minWidth; this.minHeight = _minHeight; - maximizeRestoreBtn.visible = true; + maximizeRestoreBtn.visible = false; this.resizable = true; /** diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml index cdaa8bc14b78..b92c98ab59e1 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml @@ -279,6 +279,10 @@ with BigBlueButton; if not, see . } override public function close(event:MouseEvent = null):void { + for each(var windowItf:VideoWindowItf in mutableChildrenArray) { + windowItf.close(); + } + removeAllChildren(); super.close(event); } diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index cce1c6717c35..93c03cccee3e 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -18,7 +18,7 @@ */ package org.bigbluebutton.web.controllers - +import javax.servlet.ServletRequest; import java.text.MessageFormat; import java.util.Collections; import org.apache.commons.codec.binary.Hex; @@ -592,27 +592,27 @@ class ApiController { String API_CALL = "getMeetingInfo" log.debug CONTROLLER_NAME + "#${API_CALL}" - // BEGIN - backward compatibility - if (StringUtils.isEmpty(params.checksum)) { - invalid("checksumError", "You did not pass the checksum security check") - return - } + // BEGIN - backward compatibility + if (StringUtils.isEmpty(params.checksum)) { + invalid("checksumError", "You did not pass the checksum security check") + return + } - if (StringUtils.isEmpty(params.meetingID)) { - invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting."); - return - } - - if (StringUtils.isEmpty(params.password)) { - invalid("invalidPassword","You must supply the moderator password for this call."); - return - } - - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { - invalid("checksumError", "You did not pass the checksum security check") - return - } - // END - backward compatibility + if (StringUtils.isEmpty(params.meetingID)) { + invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting."); + return + } + + if (StringUtils.isEmpty(params.password)) { + invalid("invalidPassword","You must supply the moderator password for this call."); + return + } + + if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + // END - backward compatibility ApiErrors errors = new ApiErrors() @@ -650,15 +650,15 @@ class ApiController { String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId); log.info("Retrieving meeting ${internalMeetingId}") Meeting meeting = meetingService.getMeeting(internalMeetingId); - if (meeting == null) { - // BEGIN - backward compatibility - invalid("notFound", "We could not find a meeting with that meeting ID"); - return; - // END - backward compatibility - - errors.invalidMeetingIdError(); - respondWithErrors(errors) - return; + if (meeting == null) { + // BEGIN - backward compatibility + invalid("notFound", "We could not find a meeting with that meeting ID"); + return; + // END - backward compatibility + + errors.invalidMeetingIdError(); + respondWithErrors(errors) + return; } if (meeting.getModeratorPassword().equals(modPW) == false) { @@ -682,17 +682,17 @@ class ApiController { String API_CALL = "getMeetings" log.debug CONTROLLER_NAME + "#${API_CALL}" - // BEGIN - backward compatibility - if (StringUtils.isEmpty(params.checksum)) { - invalid("checksumError", "You did not pass the checksum security check") - return - } - - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { - invalid("checksumError", "You did not pass the checksum security check") - return - } - // END - backward compatibility + // BEGIN - backward compatibility + if (StringUtils.isEmpty(params.checksum)) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + + if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + // END - backward compatibility ApiErrors errors = new ApiErrors() @@ -742,8 +742,8 @@ class ApiController { mtgs.each { m -> meeting() { meetingID(m.getExternalId()) - meetingName(m.getName()) - createTime(m.getCreateTime()) + meetingName(m.getName()) + createTime(m.getCreateTime()) attendeePW(m.getViewerPassword()) moderatorPW(m.getModeratorPassword()) hasBeenForciblyEnded(m.isForciblyEnded() ? "true" : "false") @@ -782,94 +782,112 @@ class ApiController { } + private Map getParameters(ServletRequest request) { + // Copy the parameters into our own Map as we can't pass the paramMap + // from the request as it's an unmodifiable map. + Map reqParams = new HashMap(); + Map unModReqParams = request.getParameterMap(); + + SortedSet keys = new TreeSet(unModReqParams.keySet()); + + for (String key: keys) { + reqParams.put(key, unModReqParams.get(key)); + } + + return reqParams; + } + /*********************************************** * CONFIG API ***********************************************/ def setConfigXML = { String API_CALL = "setConfigXML" log.debug CONTROLLER_NAME + "#${API_CALL}" - log.debug params - - if (StringUtils.isEmpty(params.checksum)) { - invalid("checksumError", "You did not pass the checksum security check") - return - } - - if (StringUtils.isEmpty(params.configXML)) { - invalid("configXMLError", "You did not pass a config XML") - return - } - if (StringUtils.isEmpty(params.meetingID)) { - invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting."); - return - } + if (StringUtils.isEmpty(params.checksum)) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + + if (StringUtils.isEmpty(params.configXML)) { + invalid("configXMLError", "You did not pass a config XML") + return + } + + if (StringUtils.isEmpty(params.meetingID)) { + invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting."); + return + } - // Translate the external meeting id into an internal meeting id. - String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(params.meetingID); - Meeting meeting = meetingService.getMeeting(internalMeetingId); - if (meeting == null) { - // BEGIN - backward compatibility - invalid("invalidMeetingIdentifier", "The meeting ID that you supplied did not match any existing meetings"); - return; - // END - backward compatibility + // Translate the external meeting id into an internal meeting id. + String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(params.meetingID); + Meeting meeting = meetingService.getMeeting(internalMeetingId); + if (meeting == null) { + // BEGIN - backward compatibility + invalid("invalidMeetingIdentifier", "The meeting ID that you supplied did not match any existing meetings"); + return; + // END - backward compatibility errors.invalidMeetingIdError(); respondWithErrors(errors) return; } - - String configXML = params.configXML - String decodedConfigXML; + Map reqParams = getParameters(request) + + String configXML = params.configXML + + String decodedConfigXML; - try { - decodedConfigXML = URLDecoder.decode(configXML,"UTF-8"); - } catch (UnsupportedEncodingException e) { - log.error("Couldn't decode config XML."); - invalid("configXMLError", "Cannot decode config XML") - return; - } + try { + decodedConfigXML = URLDecoder.decode(configXML, "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error("Couldn't decode config XML."); + invalid("configXMLError", "Cannot decode config XML") + return; + } - if (! paramsProcessorUtil.isConfigXMLChecksumSame(params.meetingID, configXML, params.checksum)) { + if (! paramsProcessorUtil.isPostChecksumSame(API_CALL, reqParams)) { response.addHeader("Cache-Control", "no-cache") withFormat { - xml { - render(contentType:"text/xml") { - response() { - returncode("FAILED") - messageKey("configXMLChecksumError") - message("configXMLChecksumError: request did not pass the checksum security check.") - } - } - } + xml { + render(contentType:"text/xml") { + response() { + returncode("FAILED") + messageKey("configXMLChecksumError") + message("configXMLChecksumError: request did not pass the checksum security check.") + } + } + } } } else { - println "**************** CHECKSUM PASSED **************************" + //println "**************** CHECKSUM PASSED **************************" boolean defaultConfig = false; - if(! StringUtils.isEmpty(params.defaultConfig)) { - try{ + if (! StringUtils.isEmpty(params.defaultConfig)) { + try { defaultConfig = Boolean.parseBoolean(params.defaultConfig); - }catch(Exception e) { + } catch(Exception e) { defaultConfig = false; } } + //println "[" + decodedConfigXML + "]"; + String token = meeting.storeConfig(defaultConfig, decodedConfigXML); response.addHeader("Cache-Control", "no-cache") withFormat { xml { - println "**************** CHECKSUM PASSED - XML RESPONSE **************************" + //println "**************** CHECKSUM PASSED - XML RESPONSE **************************" render(contentType:"text/xml") { - response() { - returncode("SUCCESS") - configToken(token) - } + response() { + returncode("SUCCESS") + configToken(token) + } } } - } - } + } + } } /*********************************************** diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java index 1f9d445392c6..e73570c51496 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java @@ -19,14 +19,18 @@ package org.bigbluebutton.api; +import javax.servlet.ServletRequest; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; @@ -513,6 +517,65 @@ public boolean isChecksumSame(String apiCall, String checksum, String queryStrin return true; } + public boolean isPostChecksumSame(String apiCall, HashMap params) { + if (StringUtils.isEmpty(securitySalt)) { + log.warn("Security is disabled in this service. Make sure this is intentional."); + return true; + } + + StringBuffer csbuf = new StringBuffer(); + csbuf.append(apiCall); + + SortedSet keys = new TreeSet(params.keySet()); + + boolean first = true; + String checksum = null; + for (String key: keys) { + if (key.equals("checksum")) { + // Don't include the "checksum" parameter in the checksum + checksum = params.get(key)[0]; + continue; + } + + for (String value: params.get(key)) { + if (first) { + first = false; + } else { + csbuf.append("&"); + } + csbuf.append(key); + csbuf.append("="); + String encResult; + + try { + // we need to re-encode the values because Grails unencoded it + // when it received the 'POST'ed data. Might not need to do in a GET request. + encResult = URLEncoder.encode(value, "UTF-8"); + } catch (UnsupportedEncodingException e) { + encResult = value; + } + + csbuf.append(encResult); + } + } + csbuf.append(securitySalt); + + String baseString = csbuf.toString(); + + // System.out.println( "POST basestring = [" + baseString + "]"); + + String cs = DigestUtils.shaHex(baseString); + //System.out.println("our checksum: [" + cs + "], client: [" + checksum + "]"); + //log.debug("our checksum: [{}], client: [{}]", cs, checksum); + + if (cs == null || cs.equals(checksum) == false) { + log.info("checksumError: request did not pass the checksum security check"); + return false; + } + log.debug("checksum ok: request passed the checksum security check"); + return true; + } + /************************************************* * Setters ************************************************/