Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kick_all requests and possibility to remove PIN code to both Audiobridge and Streaming plugins #2978

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);
lminiero marked this conversation as resolved.
Show resolved Hide resolved
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
152 changes: 145 additions & 7 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);
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
continue;
lminiero marked this conversation as resolved.
Show resolved Hide resolved
}
g_atomic_int_set(&s->stopping, 1);
g_atomic_int_set(&s->started, 0);
g_atomic_int_set(&s->paused, 0);
s->mountpoint = NULL;
lminiero marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -4516,7 +4652,9 @@ 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_mutex_unlock(&session->mutex);
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
janus_refcount_decrease(&s->ref);
janus_refcount_decrease(&mp->ref);
}
json_decref(event);
janus_mutex_unlock(&mp->mutex);
Expand Down