GUACAMOLE-25: Add stub "guacai" plugin for AUDIO_INPUT channel support.
This commit is contained in:
parent
ec93a2989f
commit
21ab9d765d
@ -24,6 +24,7 @@ lib_LTLIBRARIES = libguac-client-rdp.la
|
|||||||
|
|
||||||
libguac_client_rdp_la_SOURCES = \
|
libguac_client_rdp_la_SOURCES = \
|
||||||
_generated_keymaps.c \
|
_generated_keymaps.c \
|
||||||
|
audio_input.c \
|
||||||
client.c \
|
client.c \
|
||||||
input.c \
|
input.c \
|
||||||
rdp.c \
|
rdp.c \
|
||||||
@ -44,6 +45,9 @@ libguac_client_rdp_la_SOURCES = \
|
|||||||
unicode.c \
|
unicode.c \
|
||||||
user.c
|
user.c
|
||||||
|
|
||||||
|
guacai_sources = \
|
||||||
|
guac_ai/ai_service.c
|
||||||
|
|
||||||
guacsvc_sources = \
|
guacsvc_sources = \
|
||||||
guac_svc/svc_service.c \
|
guac_svc/svc_service.c \
|
||||||
rdp_svc.c
|
rdp_svc.c
|
||||||
@ -68,6 +72,7 @@ guacdr_sources = \
|
|||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
compat/client-cliprdr.h \
|
compat/client-cliprdr.h \
|
||||||
compat/rail.h \
|
compat/rail.h \
|
||||||
|
guac_ai/ai_service.h \
|
||||||
guac_rdpdr/rdpdr_fs_messages.h \
|
guac_rdpdr/rdpdr_fs_messages.h \
|
||||||
guac_rdpdr/rdpdr_fs_messages_dir_info.h \
|
guac_rdpdr/rdpdr_fs_messages_dir_info.h \
|
||||||
guac_rdpdr/rdpdr_fs_messages_file_info.h \
|
guac_rdpdr/rdpdr_fs_messages_file_info.h \
|
||||||
@ -79,6 +84,7 @@ noinst_HEADERS = \
|
|||||||
guac_rdpsnd/rdpsnd_messages.h \
|
guac_rdpsnd/rdpsnd_messages.h \
|
||||||
guac_rdpsnd/rdpsnd_service.h \
|
guac_rdpsnd/rdpsnd_service.h \
|
||||||
guac_svc/svc_service.h \
|
guac_svc/svc_service.h \
|
||||||
|
audio_input.h \
|
||||||
client.h \
|
client.h \
|
||||||
input.h \
|
input.h \
|
||||||
rdp.h \
|
rdp.h \
|
||||||
@ -104,6 +110,7 @@ noinst_HEADERS = \
|
|||||||
if ! ENABLE_WINPR
|
if ! ENABLE_WINPR
|
||||||
noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h
|
noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h
|
||||||
libguac_client_rdp_la_SOURCES += compat/winpr-stream.c
|
libguac_client_rdp_la_SOURCES += compat/winpr-stream.c
|
||||||
|
guacai_sources += compat/winpr-stream.c
|
||||||
guacsvc_sources += compat/winpr-stream.c
|
guacsvc_sources += compat/winpr-stream.c
|
||||||
guacsnd_sources += compat/winpr-stream.c
|
guacsnd_sources += compat/winpr-stream.c
|
||||||
guacdr_sources += compat/winpr-stream.c
|
guacdr_sources += compat/winpr-stream.c
|
||||||
@ -148,6 +155,25 @@ guacdr_libadd = \
|
|||||||
@COMMON_LTLIB@ \
|
@COMMON_LTLIB@ \
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
|
#
|
||||||
|
# Audio Input
|
||||||
|
#
|
||||||
|
|
||||||
|
guacai_cflags = \
|
||||||
|
-Werror -Wall -Iinclude \
|
||||||
|
@COMMON_INCLUDE@ \
|
||||||
|
@COMMON_SSH_INCLUDE@ \
|
||||||
|
@LIBGUAC_INCLUDE@
|
||||||
|
|
||||||
|
guacai_ldflags = \
|
||||||
|
-module -avoid-version -shared \
|
||||||
|
@PTHREAD_LIBS@ \
|
||||||
|
@RDP_LIBS@
|
||||||
|
|
||||||
|
guacai_libadd = \
|
||||||
|
@COMMON_LTLIB@ \
|
||||||
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
#
|
#
|
||||||
# RDPSND
|
# RDPSND
|
||||||
#
|
#
|
||||||
@ -224,10 +250,16 @@ if LEGACY_FREERDP_EXTENSIONS
|
|||||||
|
|
||||||
# FreeRDP 1.0-style extensions
|
# FreeRDP 1.0-style extensions
|
||||||
freerdp_LTLIBRARIES = \
|
freerdp_LTLIBRARIES = \
|
||||||
|
guacai.la \
|
||||||
guacdr.la \
|
guacdr.la \
|
||||||
guacsnd.la \
|
guacsnd.la \
|
||||||
guacsvc.la
|
guacsvc.la
|
||||||
|
|
||||||
|
guacai_la_SOURCES = ${guacai_sources}
|
||||||
|
guacai_la_CFLAGS = ${guacai_cflags}
|
||||||
|
guacai_la_LDFLAGS = ${guacai_ldflags}
|
||||||
|
guacai_la_LIBADD = ${guacai_libadd}
|
||||||
|
|
||||||
guacdr_la_SOURCES = ${guacdr_sources}
|
guacdr_la_SOURCES = ${guacdr_sources}
|
||||||
guacdr_la_CFLAGS = ${guacdr_cflags}
|
guacdr_la_CFLAGS = ${guacdr_cflags}
|
||||||
guacdr_la_LDFLAGS = ${guacdr_ldflags}
|
guacdr_la_LDFLAGS = ${guacdr_ldflags}
|
||||||
@ -247,10 +279,16 @@ else
|
|||||||
|
|
||||||
# FreeRDP 1.1 (and hopefully onward) extensions
|
# FreeRDP 1.1 (and hopefully onward) extensions
|
||||||
freerdp_LTLIBRARIES = \
|
freerdp_LTLIBRARIES = \
|
||||||
|
guacai-client.la \
|
||||||
guacdr-client.la \
|
guacdr-client.la \
|
||||||
guacsnd-client.la \
|
guacsnd-client.la \
|
||||||
guacsvc-client.la
|
guacsvc-client.la
|
||||||
|
|
||||||
|
guacai_client_la_SOURCES = ${guacai_sources}
|
||||||
|
guacai_client_la_CFLAGS = ${guacai_cflags}
|
||||||
|
guacai_client_la_LDFLAGS = ${guacai_ldflags}
|
||||||
|
guacai_client_la_LIBADD = ${guacai_libadd}
|
||||||
|
|
||||||
guacdr_client_la_SOURCES = ${guacdr_sources}
|
guacdr_client_la_SOURCES = ${guacdr_sources}
|
||||||
guacdr_client_la_CFLAGS = ${guacdr_cflags}
|
guacdr_client_la_CFLAGS = ${guacdr_cflags}
|
||||||
guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
|
guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
|
||||||
|
74
src/protocols/rdp/audio_input.c
Normal file
74
src/protocols/rdp/audio_input.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "audio_input.h"
|
||||||
|
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/channels/channels.h>
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int guac_rdp_audio_handler(guac_user* user, guac_stream* stream,
|
||||||
|
char* mimetype) {
|
||||||
|
|
||||||
|
/* FIXME: Assuming mimetype of "audio/L16;rate=44100,channels=2" */
|
||||||
|
|
||||||
|
/* Init stream data */
|
||||||
|
stream->blob_handler = guac_rdp_audio_blob_handler;
|
||||||
|
stream->end_handler = guac_rdp_audio_end_handler;
|
||||||
|
|
||||||
|
guac_protocol_send_ack(user->socket, stream,
|
||||||
|
"OK", GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream,
|
||||||
|
void* data, int length) {
|
||||||
|
|
||||||
|
/* STUB */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) {
|
||||||
|
|
||||||
|
/* STUB */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_rdp_audio_load_plugin(rdpContext* context) {
|
||||||
|
|
||||||
|
/* Add "AUDIO_INPUT" channel */
|
||||||
|
ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV));
|
||||||
|
args->argc = 1;
|
||||||
|
args->argv = malloc(sizeof(char**) * 1);
|
||||||
|
args->argv[0] = strdup("guacai");
|
||||||
|
freerdp_dynamic_channel_collection_add(context->settings, args);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
54
src/protocols/rdp/audio_input.h
Normal file
54
src/protocols/rdp/audio_input.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_AUDIO_INPUT_H
|
||||||
|
#define GUAC_RDP_AUDIO_INPUT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for inbound audio data (audio input).
|
||||||
|
*/
|
||||||
|
guac_user_audio_handler guac_rdp_audio_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for stream data related to audio input.
|
||||||
|
*/
|
||||||
|
guac_user_blob_handler guac_rdp_audio_blob_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for end-of-stream related to audio input.
|
||||||
|
*/
|
||||||
|
guac_user_end_handler guac_rdp_audio_end_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads Guacamole's "guacai" plugin for FreeRDP, adding support for the
|
||||||
|
* "AUDIO_INPUT" dynamic virtual channel. This function must ONLY be called
|
||||||
|
* after FreeRDP's "drdynvc" virtual channel plugin has been loaded.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The rdpContext associated with the active RDP session.
|
||||||
|
*/
|
||||||
|
void guac_rdp_audio_load_plugin(rdpContext* context);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
56
src/protocols/rdp/guac_ai/ai_service.c
Normal file
56
src/protocols/rdp/guac_ai/ai_service.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "ai_service.h"
|
||||||
|
#include "rdp.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#include <freerdp/constants.h>
|
||||||
|
#include <freerdp/dvc.h>
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_WINPR
|
||||||
|
#include <winpr/stream.h>
|
||||||
|
#else
|
||||||
|
#include "compat/winpr-stream.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point for AUDIO_INPUT dynamic virtual channel.
|
||||||
|
*/
|
||||||
|
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
|
||||||
|
|
||||||
|
rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
|
||||||
|
freerdp* instance = settings->instance;
|
||||||
|
rdpContext* context = instance->context;
|
||||||
|
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||||
|
|
||||||
|
/* STUB */
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"STUB: AUDIO_INPUT DVC (entry point)");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
29
src/protocols/rdp/guac_ai/ai_service.h
Normal file
29
src/protocols/rdp/guac_ai/ai_service.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_AI_SERVICE_H
|
||||||
|
#define GUAC_RDP_AI_SERVICE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* STUB */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "audio_input.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "guac_cursor.h"
|
#include "guac_cursor.h"
|
||||||
#include "guac_display.h"
|
#include "guac_display.h"
|
||||||
@ -223,23 +224,32 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
|||||||
(pChannelConnectedEventHandler) guac_rdp_channel_connected);
|
(pChannelConnectedEventHandler) guac_rdp_channel_connected);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
/* Load DRDYNVC plugin if required */
|
||||||
/* Load required plugins if display update is enabled */
|
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE
|
||||||
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
|
|| settings->enable_audio_input) {
|
||||||
|
|
||||||
/* Load virtual channel management plugin (needed by display update) */
|
/* Load virtual channel management plugin */
|
||||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||||
"drdynvc", instance->settings))
|
"drdynvc", instance->settings))
|
||||||
guac_client_log(client, GUAC_LOG_WARNING,
|
guac_client_log(client, GUAC_LOG_WARNING,
|
||||||
"Failed to load drdynvc plugin. Display update support "
|
"Failed to load drdynvc plugin. Display update and audio "
|
||||||
"will be disabled.");
|
"input support will be disabled.");
|
||||||
|
|
||||||
/* Init display update plugin if "drdynvc" was loaded successfully */
|
/* Init display update plugin if "drdynvc" was loaded successfully */
|
||||||
else
|
else {
|
||||||
|
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||||
|
/* Load "disp" plugin for display update */
|
||||||
|
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
|
||||||
guac_rdp_disp_load_plugin(instance->context);
|
guac_rdp_disp_load_plugin(instance->context);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Load "AUDIO_INPUT" plugin for audio input*/
|
||||||
|
if (settings->enable_audio_input)
|
||||||
|
guac_rdp_audio_load_plugin(instance->context);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
} /* end if drdynvc required */
|
||||||
|
|
||||||
/* Load clipboard plugin */
|
/* Load clipboard plugin */
|
||||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||||
|
@ -90,6 +90,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
|
|||||||
"recording-name",
|
"recording-name",
|
||||||
"create-recording-path",
|
"create-recording-path",
|
||||||
"resize-method",
|
"resize-method",
|
||||||
|
"enable-audio-input",
|
||||||
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
@ -378,6 +379,12 @@ enum RDP_ARGS_IDX {
|
|||||||
*/
|
*/
|
||||||
IDX_RESIZE_METHOD,
|
IDX_RESIZE_METHOD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "true" if audio input (microphone) should be enabled for the RDP
|
||||||
|
* connection, "false" or blank otherwise.
|
||||||
|
*/
|
||||||
|
IDX_ENABLE_AUDIO_INPUT,
|
||||||
|
|
||||||
RDP_ARGS_COUNT
|
RDP_ARGS_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -739,6 +746,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
|||||||
settings->resize_method = GUAC_RESIZE_NONE;
|
settings->resize_method = GUAC_RESIZE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Audio input enable/disable */
|
||||||
|
settings->enable_audio_input =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||||
|
IDX_ENABLE_AUDIO_INPUT, 0);
|
||||||
|
|
||||||
/* Success */
|
/* Success */
|
||||||
return settings;
|
return settings;
|
||||||
|
|
||||||
|
@ -378,6 +378,11 @@ typedef struct guac_rdp_settings {
|
|||||||
*/
|
*/
|
||||||
guac_rdp_resize_method resize_method;
|
guac_rdp_resize_method resize_method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether audio input (microphone) is enabled.
|
||||||
|
*/
|
||||||
|
int enable_audio_input;
|
||||||
|
|
||||||
} guac_rdp_settings;
|
} guac_rdp_settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "audio_input.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "guac_display.h"
|
#include "guac_display.h"
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
@ -65,6 +66,10 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle inbound audio streams if audio input is enabled */
|
||||||
|
if (settings->enable_audio_input)
|
||||||
|
user->audio_handler = guac_rdp_audio_handler;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not owner, synchronize with current state */
|
/* If not owner, synchronize with current state */
|
||||||
|
Loading…
Reference in New Issue
Block a user