Merge pull request #12877 from antobinary/merge-23-july-30

Merge 2.3.9 into 'develop'
This commit is contained in:
Anton Georgiev 2021-07-30 19:45:07 -04:00 committed by GitHub
commit 92e932b5cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 66 deletions

View File

@ -19,8 +19,12 @@
package org.bigbluebutton.presentation.imp;
import java.util.Timer;
import java.util.TimerTask;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -28,57 +32,63 @@ import org.slf4j.LoggerFactory;
/**
* A wrapper class the executes an external command.
*
* See http://kylecartmell.com/?p=9
*
* @author Richard Alam
*
* @author Marcel Hellkamp
*/
public class ExternalProcessExecutor {
private static Logger log = LoggerFactory.getLogger(ExternalProcessExecutor.class);
// Replace with ProcessBuilder.Redirect.DISCARD in java 9+
private static File DISCARD = new File(
System.getProperty("os.name").startsWith("Windows") ? "NUL" : "/dev/null");
/**
* Run COMMAND for at most timeoutMillis while ignoring any output.
*
* @deprecated The COMMAND string is split on whitespace to create an argument
* list. This won't work for arguments that contain whitespace. Use
* {@link #exec(List, Duration)} instead.
*
* @param COMMAND A single command or whitespace separated list of
* arguments.
* @param timeoutMillis Timeout in milliseconds.
* @return true if the command terminated in time with an exit value of 0.
*/
@Deprecated
public boolean exec(String COMMAND, long timeoutMillis) {
Timer timer = new Timer(false);
Process p = null;
return exec(Arrays.asList(COMMAND.split("[ \\t\\n\\r\\f]+")), Duration.ofMillis(timeoutMillis));
}
/**
* Run a command for a limited amount of time while ignoring any output.
*
* @param cmd List containing the program and its arguments.
* @param timeout Maximum execution time.
* @return true if the command terminated in time with an exit value of 0.
*/
public boolean exec(List<String> cmd, Duration timeout) {
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectError(DISCARD);
pb.redirectOutput(DISCARD);
Process proc;
try {
InterruptTimerTask interrupter = new InterruptTimerTask(Thread.currentThread());
timer.schedule(interrupter, timeoutMillis);
p = Runtime.getRuntime().exec(COMMAND);
int result = p.waitFor();
if (result == 0) {
return true;
} else {
proc = pb.start();
} catch (IOException e) {
log.error("Failed to execute: {}", String.join(" ", cmd), e);
return false;
}
} catch(Exception e) {
log.info("TIMEDOUT excuting : {}", COMMAND);
if (p != null) {
p.destroy();
}
} finally {
timer.cancel(); // If the process returns within the timeout period, we have to stop the interrupter
// so that it does not unexpectedly interrupt some other code later.
Thread.interrupted(); // We need to clear the interrupt flag on the current thread just in case
// interrupter executed after waitFor had already returned but before timer.cancel
// took effect.
//
// Oh, and there's also Sun bug 6420270 to worry about here.
try {
if (!proc.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
log.warn("TIMEDOUT excuting: {}", String.join(" ", cmd));
proc.destroy();
}
return !proc.isAlive() && proc.exitValue() == 0;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
proc.destroy();
return false;
}
class InterruptTimerTask extends TimerTask {
private Thread thread;
public InterruptTimerTask(Thread t) {
this.thread = t;
}
public void run() {
thread.interrupt();
}
}
}

View File

@ -1,2 +1 @@
BIGBLUEBUTTON_RELEASE=2.4-beta-1

View File

@ -45,7 +45,28 @@ function breakouts(role) {
],
};
return Breakouts.find(selector);
const fields = {
fields: {
users: {
$elemMatch: {
// do not allow users to obtain 'redirectToHtml5JoinURL' for others
userId,
},
},
breakoutId: 1,
externalId: 1,
freeJoin: 1,
isDefaultName: 1,
joinedUsers: 1,
name: 1,
parentMeetingId: 1,
sequence: 1,
shortName: 1,
timeRemaining: 1,
},
};
return Breakouts.find(selector, fields);
}
function publish(...args) {

View File

@ -11,8 +11,6 @@ const logConnectionStatus = (meetingId, userId, status, type, value) => {
Logger.info(`Connection status updated: meetingId=${meetingId} userId=${userId} status=${status} type=${type}`);
break;
case 'warning':
// Skip
break;
case 'danger':
case 'critical':
switch (type) {

View File

@ -25,7 +25,7 @@ function groupChatMsg(chatsIds) {
timestamp: { $gte: User.authTokenValidatedTime },
$or: [
{ meetingId, chatId: { $eq: PUBLIC_GROUP_CHAT_ID } },
{ chatId: { $in: chatsIds } },
{ meetingId, chatId: { $in: chatsIds } },
],
};
return GroupChatMsg.find(selector);

View File

@ -1,18 +1,30 @@
import GuestUsers from '/imports/api/guest-users/';
import Users from '/imports/api/users';
import { Meteor } from 'meteor/meteor';
import Logger from '/imports/startup/server/logger';
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
function guestUsers() {
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
Logger.warn(`Publishing GuestUsers was requested by unauth connection ${this.connection.id}`);
Logger.warn(`Publishing GuestUser was requested by unauth connection ${this.connection.id}`);
return GuestUsers.find({ meetingId: '' });
}
const { meetingId, userId } = tokenValidation;
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
if (!User || User.role !== ROLE_MODERATOR) {
Logger.warn(
'Publishing current-poll was requested by non-moderator connection',
{ meetingId, userId, connectionId: this.connection.id },
);
return GuestUsers.find({ meetingId: '' });
}
Logger.debug(`Publishing GuestUsers for ${meetingId} ${userId}`);
return GuestUsers.find({ meetingId });

View File

@ -1,18 +1,39 @@
import { Meteor } from 'meteor/meteor';
import Logger from '/imports/startup/server/logger';
import Users from '/imports/api/users';
import Polls from '/imports/api/polls';
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
import AuthTokenValidation, {
ValidationStates,
} from '/imports/api/auth-token-validation';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
function currentPoll(secretPoll) {
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
const tokenValidation = AuthTokenValidation.findOne({
connectionId: this.connection.id,
});
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
Logger.warn(`Publishing Polls was requested by unauth connection ${this.connection.id}`);
if (
!tokenValidation ||
tokenValidation.validationStatus !== ValidationStates.VALIDATED
) {
Logger.warn(
`Publishing Polls was requested by unauth connection ${this.connection.id}`
);
return Polls.find({ meetingId: '' });
}
const { meetingId, userId } = tokenValidation;
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
if (!User || User.role !== ROLE_MODERATOR) {
Logger.warn(
'Publishing current-poll was requested by non-moderator connection',
{ meetingId, userId, connectionId: this.connection.id }
);
return Polls.find({ meetingId: '' });
}
Logger.debug('Publishing Polls', { meetingId, userId });
const selector = {
@ -38,10 +59,17 @@ function publishCurrentPoll(...args) {
Meteor.publish('current-poll', publishCurrentPoll);
function polls() {
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
const tokenValidation = AuthTokenValidation.findOne({
connectionId: this.connection.id,
});
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
Logger.warn(`Publishing Polls was requested by unauth connection ${this.connection.id}`);
if (
!tokenValidation ||
tokenValidation.validationStatus !== ValidationStates.VALIDATED
) {
Logger.warn(
`Publishing Polls was requested by unauth connection ${this.connection.id}`
);
return Polls.find({ meetingId: '' });
}

View File

@ -86,6 +86,7 @@ class Pad extends PureComponent {
this.toggleListen = this.toggleListen.bind(this);
this.handleListen = this.handleListen.bind(this);
if (this.recognition) {
this.recognition.addEventListener('end', () => {
const { listening } = this.state;
if (listening) {
@ -94,6 +95,7 @@ class Pad extends PureComponent {
}
});
}
}
componentDidUpdate() {
const {
@ -103,8 +105,13 @@ class Pad extends PureComponent {
} = this.props;
if (this.recognition) {
if (ownerId !== currentUserId) {
this.recognition.stop();
} else if (this.state.listening && this.recognition.lang !== locale) {
this.recognition.stop();
this.stopListen();
}
this.recognition.lang = locale;
if (ownerId !== currentUserId) this.recognition.stop();
}
}