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

129 changes: 125 additions & 4 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 @@ -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_lock(&audiobridge->mutex);
janus_mutex_unlock(&rooms_mutex);
lminiero marked this conversation as resolved.
Show resolved Hide resolved
/* 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
146 changes: 142 additions & 4 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 generic \c ok is returned:
*
\verbatim
{
"streaming" : "ok",
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
}
\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;
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
/* 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);
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
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) {
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_mutex_unlock(&s->mutex);
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
}
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("ok"));
mikaelnousiainen marked this conversation as resolved.
Show resolved Hide resolved
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