From 7ba6bd9f7a197418e958b998148da0b8dd7211f6 Mon Sep 17 00:00:00 2001
From: basisbit <hp.stefan@gmail.com>
Date: Wed, 9 Dec 2020 13:58:26 +0100
Subject: [PATCH] Fix voiceBridge collision

Cherry-picked the commits from https://github.com/bigbluebutton/bigbluebutton/pull/9251
The added code checks if a meetingID is unique and makes sure no two meetings use the same VoiceBridge. Also see Issue # 9024
---
 .../org/bigbluebutton/api/MeetingService.java | 32 +++++++++++++++++--
 .../web/controllers/ApiController.groovy      | 22 +++++++++++++
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
index d2a3ee7ad7..d73e1783bf 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
@@ -290,8 +290,10 @@ public class MeetingService implements MessageListener {
 
   public synchronized boolean createMeeting(Meeting m) {
     String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(m.getExternalId());
-    Meeting existing = getNotEndedMeetingWithId(internalMeetingId);
-    if (existing == null) {
+    Meeting existingId = getNotEndedMeetingWithId(internalMeetingId);
+    Meeting existingTelVoice = getNotEndedMeetingWithTelVoice(m.getTelVoice());
+    Meeting existingWebVoice = getNotEndedMeetingWithWebVoice(m.getWebVoice());
+    if (existingId == null && existingTelVoice == null && existingWebVoice == null) {
       meetings.put(m.getInternalId(), m);
       handle(new CreateMeeting(m));
       return true;
@@ -450,6 +452,32 @@ public class MeetingService implements MessageListener {
       return null;
   }
 
+  public Meeting getNotEndedMeetingWithTelVoice(String telVoice) {
+      if (telVoice == null)
+          return null;
+      for (Map.Entry<String, Meeting> entry : meetings.entrySet()) {
+          Meeting m = entry.getValue();
+          if (m.getTelVoice() == telVoice) {
+              if (!m.isForciblyEnded())
+                  return m;
+          }
+      }
+      return null;
+  }
+
+  public Meeting getNotEndedMeetingWithWebVoice(String webVoice) {
+      if (webVoice == null)
+          return null;
+      for (Map.Entry<String, Meeting> entry : meetings.entrySet()) {
+          Meeting m = entry.getValue();
+          if (m.getWebVoice() == webVoice) {
+              if (!m.isForciblyEnded())
+                  return m;
+          }
+      }
+      return null;
+  }
+
   public Boolean validateTextTrackSingleUseToken(String recordId, String caption, String token) {
     return recordingService.validateTextTrackSingleUseToken(recordId, caption, token);
   }
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 227ba2b3fc..221dd716b9 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
@@ -136,6 +136,20 @@ class ApiController {
       return
     }
 
+    // Ensure unique TelVoice. Uniqueness is not guaranteed by paramsProcessorUtil.
+    if (!params.voiceBridge) {
+      // Try up to 10 times. We should find a valid telVoice quickly unless
+      // the server hosts ~100k meetings (default 5-digit telVoice)
+      for (int i in 1..10) {
+        String telVoice = paramsProcessorUtil.processTelVoice("");
+        if (!meetingService.getNotEndedMeetingWithTelVoice(telVoice)) {
+          params.voiceBridge = telVoice;
+          break;
+        }
+      }
+      // Still no unique voiceBridge found? Let createMeeting handle it.
+    }
+
     Meeting newMeeting = paramsProcessorUtil.processCreateParams(params)
 
     if (meetingService.createMeeting(newMeeting)) {
@@ -167,6 +181,14 @@ class ApiController {
         }
 
         return
+      } else {
+        Meeting existingTelVoice = meetingService.getNotEndedMeetingWithTelVoice(newMeeting.getTelVoice());
+        Meeting existingWebVoice = meetingService.getNotEndedMeetingWithWebVoice(newMeeting.getWebVoice());
+        if (existingTelVoice != null || existingWebVoice != null) {
+          log.error "VoiceBridge already in use by another meeting (different meetingId)"
+          errors.nonUniqueMeetingIdError()
+          respondWithErrors(errors)
+        }
       }
     }
   }
-- 
GitLab