Skip to content

Commit

Permalink
Add kick_all requests and possibility to remove PIN code to both Audi…
Browse files Browse the repository at this point in the history
…obridge and Streaming plugins (#2978)
  • Loading branch information
mikaelnousiainen committed Jun 8, 2022
1 parent c16a77e commit e5b25bb
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 12 deletions.
133 changes: 127 additions & 6 deletions src/plugins/janus_audiobridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ room-<unique room ID>: {
"secret" : "<room secret, mandatory if configured>",
"new_description" : "<new pretty name of the room, optional>",
"new_secret" : "<new password required to edit/destroy the room, optional>",
"new_pin" : "<new password required to join the room, optional>",
"new_pin" : "<new PIN required to join the room, PIN will be removed if set to an empty string, optional>",
"new_is_private" : <true|false, whether the room should appear in a list request>,
"new_record_dir" : "<new path where new recording files should be saved>",
"new_mjrs_dir" : "<new path where new MJR files should be saved>",
Expand Down Expand Up @@ -354,6 +354,29 @@ room-<unique room ID>: {
{
"audiobridge" : "success",
}
\endverbatim
*
* If you're the administrator of a room (that is, you created it and have access
* to the secret) you can kick all participants using the \c kick_all request. Notice
* that this only kicks all users out of the room, but does not prevent them from
* re-joining: to ban them, you need to first remove them from the list of
* authorized users (see \c allowed request) and then perform \c kick_all.
* The \c kick_all request has to be formatted as follows:
*
\verbatim
{
"request" : "kick_all",
"secret" : "<room secret, mandatory if configured>",
"room" : <unique numeric ID of the room>
}
\endverbatim
*
* A successful request will result in a \c success response:
*
\verbatim
{
"audiobridge" : "success",
}
\endverbatim
*
* To get a list of the available rooms (excluded those configured or
Expand Down Expand Up @@ -3476,10 +3499,14 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
audiobridge->room_secret = new_secret;
g_free(old_secret);
}
if(pin && strlen(json_string_value(pin)) > 0) {
if(pin) {
char *old_pin = audiobridge->room_pin;
char *new_pin = g_strdup(json_string_value(pin));
audiobridge->room_pin = new_pin;
if(strlen(json_string_value(pin)) > 0) {
char *new_pin = g_strdup(json_string_value(pin));
audiobridge->room_pin = new_pin;
} else {
audiobridge->room_pin = NULL;
}
g_free(old_pin);
}
if(recdir) {
Expand Down Expand Up @@ -3820,7 +3847,7 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
janus_mutex_unlock(&participant->rec_mutex);
}
}
}
}
janus_mutex_unlock(&audiobridge->mutex);
janus_refcount_decrease(&audiobridge->ref);
response = json_object();
Expand Down Expand Up @@ -4349,8 +4376,8 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
goto prepare_response;
}
janus_refcount_increase(&audiobridge->ref);
janus_mutex_lock(&audiobridge->mutex);
janus_mutex_unlock(&rooms_mutex);
janus_mutex_lock(&audiobridge->mutex);
/* A secret may be required for this action */
JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
Expand Down Expand Up @@ -4412,6 +4439,100 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
janus_mutex_unlock(&audiobridge->mutex);
janus_refcount_decrease(&audiobridge->ref);
goto prepare_response;
} else if(!strcasecmp(request_text, "kick_all")) {
JANUS_LOG(LOG_VERB, "Attempt to kick all participants from an existing AudioBridge room\n");
JANUS_VALIDATE_JSON_OBJECT(root, secret_parameters,
error_code, error_cause, TRUE,
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
if(error_code != 0)
goto prepare_response;
if(!string_ids) {
JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
error_code, error_cause, TRUE,
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
} else {
JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
error_code, error_cause, TRUE,
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
}
if(error_code != 0)
goto prepare_response;
json_t *room = json_object_get(root, "room");
guint64 room_id = 0;
char room_id_num[30], *room_id_str = NULL;
if(!string_ids) {
room_id = json_integer_value(room);
g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
room_id_str = room_id_num;
} else {
room_id_str = (char *)json_string_value(room);
}
janus_mutex_lock(&rooms_mutex);
janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms,
string_ids ? (gpointer)room_id_str : (gpointer)&room_id);
if(audiobridge == NULL) {
janus_mutex_unlock(&rooms_mutex);
error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
JANUS_LOG(LOG_ERR, "No such room (%s)\n", room_id_str);
g_snprintf(error_cause, 512, "No such room (%s)", room_id_str);
goto prepare_response;
}
janus_refcount_increase(&audiobridge->ref);
janus_mutex_unlock(&rooms_mutex);
janus_mutex_lock(&audiobridge->mutex);
/* A secret may be required for this action */
JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
if(error_code != 0) {
janus_mutex_unlock(&audiobridge->mutex);
janus_refcount_decrease(&audiobridge->ref);
goto prepare_response;
}
GHashTableIter kick_iter;
gpointer kick_value;
g_hash_table_iter_init(&kick_iter, audiobridge->participants);
while(g_hash_table_iter_next(&kick_iter, NULL, &kick_value)) {
janus_audiobridge_participant *participant = kick_value;
JANUS_LOG(LOG_VERB, "Kicking participant %s (%s)\n",
participant->user_id_str, participant->display ? participant->display : "??");
guint64 user_id = 0;
char user_id_num[30], *user_id_str = NULL;
if(string_ids) {
user_id_str = participant->user_id_str;
} else {
user_id = participant->user_id;
g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
user_id_str = user_id_num;
}
/* Notify all participants about the kick */
json_t *event = json_object();
json_object_set_new(event, "audiobridge", json_string("event"));
json_object_set_new(event, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
json_object_set_new(event, "kicked_all", string_ids ? json_string(user_id_str) : json_integer(user_id));
JANUS_LOG(LOG_VERB, "Notifying participant %s (%s)\n", participant->user_id_str, participant->display ? participant->display : "??");
int ret = gateway->push_event(participant->session->handle, &janus_audiobridge_plugin, NULL, event, NULL);
JANUS_LOG(LOG_VERB, " >> %d (%s)\n", ret, janus_get_api_error(ret));
json_decref(event);
/* Also notify event handlers */
if(notify_events && gateway->events_is_enabled()) {
json_t *info = json_object();
json_object_set_new(info, "event", json_string("kicked_all"));
json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
json_object_set_new(info, "id", string_ids ? json_string(user_id_str) : json_integer(user_id));
gateway->notify_event(&janus_audiobridge_plugin, session ? session->handle : NULL, info);
}
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
if(participant && participant->session)
gateway->close_pc(participant->session->handle);
JANUS_LOG(LOG_VERB, "Kicked user %s from room %s\n", user_id_str, room_id_str);
}
/* Prepare response */
response = json_object();
json_object_set_new(response, "audiobridge", json_string("success"));
/* Done */
janus_mutex_unlock(&audiobridge->mutex);
janus_refcount_decrease(&audiobridge->ref);
goto prepare_response;
} else if(!strcasecmp(request_text, "listparticipants")) {
/* List all participants in a room */
if(!string_ids) {
Expand Down
150 changes: 144 additions & 6 deletions src/plugins/janus_streaming.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ multistream-test: {
"new_description" : "<new description for the mountpoint; optional>",
"new_metadata" : "<new metadata for the mountpoint; optional>",
"new_secret" : "<new secret for the mountpoint; optional>",
"new_pin" : "<new PIN for the mountpoint; optional>",
"new_pin" : "<new PIN for the mountpoint, PIN will be removed if set to an empty string; optional>",
"new_is_private" : <true|false, depending on whether the mountpoint should be now listable; optional>,
"permanent" : <true|false, whether the mountpoint should be saved to configuration file or not; false by default>
}
Expand Down Expand Up @@ -548,6 +548,27 @@ multistream-test: {
{
"streaming" : "ok"
}
\endverbatim
*
* You can kick all viewers from a mountpoint using the \c kick_all request. Notice
* that this only removes all viewers, but does not prevent them from starting to watch
* the mountpoint again. Please note this request works with all mountpoint types,
* except for on-demand streaming. The \c kick_all request has to be formatted as follows:
*
\verbatim
{
"request" : "kick_all",
"id" : <unique ID of the mountpoint to disable; mandatory>,
"secret" : "<mountpoint secret; mandatory if configured>",
}
\endverbatim
*
* If successful, a \c kicked_all response is returned:
*
\verbatim
{
"streaming" : "kicked_all",
}
\endverbatim
*
* Finally, you can record a mountpoint to the internal Janus .mjr format
Expand Down Expand Up @@ -4224,10 +4245,14 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
mp->secret = new_secret;
g_free(old_secret);
}
if(pin && strlen(json_string_value(pin)) > 0) {
if(pin) {
char *old_pin = mp->pin;
char *new_pin = g_strdup(json_string_value(pin));
mp->pin = new_pin;
if(strlen(json_string_value(pin)) > 0) {
char *new_pin = g_strdup(json_string_value(pin));
mp->pin = new_pin;
} else {
mp->pin = NULL;
}
g_free(old_pin);
}
if(save) {
Expand Down Expand Up @@ -4404,6 +4429,119 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
/* Done */
JANUS_LOG(LOG_VERB, "Streaming mountpoint edited\n");
goto prepare_response;
} else if(!strcasecmp(request_text, "kick_all")) {
/* Note the kick_all request works with all mountpoint types except for on-demand streaming,
* because each on-demand viewer has their own thread and their own playback context. */
if(!string_ids) {
JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
error_code, error_cause, TRUE,
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
} else {
JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
error_code, error_cause, TRUE,
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
}
if(error_code != 0)
goto prepare_response;
json_t *id = json_object_get(root, "id");
guint64 id_value = 0;
char id_num[30], *id_value_str = NULL;
if(!string_ids) {
id_value = json_integer_value(id);
g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
id_value_str = id_num;
} else {
id_value_str = (char *)json_string_value(id);
}
janus_mutex_lock(&mountpoints_mutex);
janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
if(mp == NULL) {
janus_mutex_unlock(&mountpoints_mutex);
JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
goto prepare_response;
}
janus_refcount_increase(&mp->ref);
/* A secret may be required for this action */
JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
if(error_code != 0) {
janus_refcount_decrease(&mp->ref);
janus_mutex_unlock(&mountpoints_mutex);
goto prepare_response;
}
JANUS_LOG(LOG_VERB, "Request to kick all viewers from mountpoint/stream %s\n", id_value_str);
janus_mutex_lock(&mp->mutex);
GList *viewer = g_list_first(mp->viewers);
/* Prepare JSON event */
json_t *event = json_object();
json_object_set_new(event, "streaming", json_string("event"));
json_t *result = json_object();
json_object_set_new(result, "status", json_string("kicked_all"));
json_object_set_new(event, "result", result);
while(viewer) {
janus_streaming_session *s = (janus_streaming_session *)viewer->data;
if(s == NULL) {
mp->viewers = g_list_remove_all(mp->viewers, s);
viewer = g_list_first(mp->viewers);
continue;
}
janus_mutex_lock(&s->mutex);
if(s->mountpoint != mp) {
mp->viewers = g_list_remove_all(mp->viewers, s);
viewer = g_list_first(mp->viewers);
janus_mutex_unlock(&s->mutex);
continue;
}
g_atomic_int_set(&s->stopping, 1);
g_atomic_int_set(&s->started, 0);
g_atomic_int_set(&s->paused, 0);
s->mountpoint = NULL;
janus_mutex_unlock(&s->mutex);
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
gateway->push_event(s->handle, &janus_streaming_plugin, NULL, event, NULL);
gateway->close_pc(s->handle);
if(mp->streaming_source == janus_streaming_source_rtp) {
/* Remove the viewer from the helper threads too, if any */
if(mp->helper_threads > 0) {
GList *l = mp->threads;
while(l) {
janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
janus_mutex_lock(&ht->mutex);
if(g_list_find(ht->viewers, s) != NULL) {
ht->num_viewers--;
ht->viewers = g_list_remove_all(ht->viewers, s);
janus_mutex_unlock(&ht->mutex);
JANUS_LOG(LOG_VERB, "Removing viewer from helper thread #%d (destroy)\n", ht->id);
break;
}
janus_mutex_unlock(&ht->mutex);
l = l->next;
}
}
}
mp->viewers = g_list_remove_all(mp->viewers, s);
viewer = g_list_first(mp->viewers);
janus_refcount_decrease(&s->ref);
janus_refcount_decrease(&mp->ref);
}
json_decref(event);
janus_mutex_unlock(&mp->mutex);
janus_refcount_decrease(&mp->ref);
/* Also notify event handlers */
if(notify_events && gateway->events_is_enabled()) {
json_t *info = json_object();
json_object_set_new(info, "event", json_string("kicked_all"));
json_object_set_new(info, "id", string_ids ? json_string(id_value_str) : json_integer(id_value));
gateway->notify_event(&janus_streaming_plugin, session ? session->handle : NULL, info);
}
janus_mutex_unlock(&mountpoints_mutex);
/* Send info back */
response = json_object();
json_object_set_new(response, "streaming", json_string("kicked_all"));
goto prepare_response;
} else if(!strcasecmp(request_text, "destroy")) {
/* Get rid of an existing stream (notice this doesn't remove it from the config file, though) */
JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
Expand Down Expand Up @@ -4493,8 +4631,6 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
gateway->push_event(s->handle, &janus_streaming_plugin, NULL, event, NULL);
gateway->close_pc(s->handle);
janus_refcount_decrease(&s->ref);
janus_refcount_decrease(&mp->ref);
if(mp->streaming_source == janus_streaming_source_rtp) {
/* Remove the viewer from the helper threads too, if any */
if(mp->helper_threads > 0) {
Expand All @@ -4517,6 +4653,8 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
mp->viewers = g_list_remove_all(mp->viewers, s);
viewer = g_list_first(mp->viewers);
janus_mutex_unlock(&s->mutex);
janus_refcount_decrease(&s->ref);
janus_refcount_decrease(&mp->ref);
}
json_decref(event);
janus_mutex_unlock(&mp->mutex);
Expand Down

0 comments on commit e5b25bb

Please sign in to comment.