mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
5d1efe7f55
This server uses an unknown 003.889 protocol version. This patch fixes the version validation in order to simply fallback to 3.3 as suggested by the spec.
1134 lines
31 KiB
C
1134 lines
31 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "rfbdecoder.h"
|
|
#include "d3des.h"
|
|
#include <gst/gst.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define RFB_GET_UINT32(ptr) GST_READ_UINT32_BE(ptr)
|
|
#define RFB_GET_UINT16(ptr) GST_READ_UINT16_BE(ptr)
|
|
#define RFB_GET_UINT8(ptr) GST_READ_UINT8(ptr)
|
|
|
|
#define RFB_SET_UINT32(ptr, val) GST_WRITE_UINT32_BE((ptr),(val))
|
|
#define RFB_SET_UINT16(ptr, val) GST_WRITE_UINT16_BE((ptr),(val))
|
|
#define RFB_SET_UINT8(ptr, val) GST_WRITE_UINT8((ptr),(val))
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (rfbdecoder_debug);
|
|
#define GST_CAT_DEFAULT rfbdecoder_debug
|
|
|
|
|
|
static gboolean rfb_decoder_state_wait_for_protocol_version (RfbDecoder *
|
|
decoder);
|
|
static gboolean rfb_decoder_state_wait_for_security (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_state_send_client_initialisation (RfbDecoder *
|
|
decoder);
|
|
static gboolean rfb_decoder_state_wait_for_server_initialisation (RfbDecoder *
|
|
decoder);
|
|
static gboolean rfb_decoder_state_security_result (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_state_normal (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_state_framebuffer_update (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder *
|
|
decoder);
|
|
static gboolean rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_state_server_cut_text (RfbDecoder * decoder);
|
|
static gboolean rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x,
|
|
gint start_y, gint rect_w, gint rect_h);
|
|
static gboolean rfb_decoder_copyrect_encoding (RfbDecoder * decoder,
|
|
gint start_x, gint start_y, gint rect_w, gint rect_h);
|
|
static gboolean rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x,
|
|
gint start_y, gint rect_w, gint rect_h);
|
|
static gboolean rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x,
|
|
gint start_y, gint rect_w, gint rect_h);
|
|
static gboolean rfb_decoder_hextile_encoding (RfbDecoder * decoder,
|
|
gint start_x, gint start_y, gint rect_w, gint rect_h);
|
|
|
|
RfbDecoder *
|
|
rfb_decoder_new (void)
|
|
{
|
|
RfbDecoder *decoder = g_new0 (RfbDecoder, 1);
|
|
|
|
decoder->socket_client = g_socket_client_new ();
|
|
decoder->connection = NULL;
|
|
decoder->cancellable = g_cancellable_new ();
|
|
|
|
decoder->password = NULL;
|
|
|
|
decoder->use_copyrect = FALSE;
|
|
|
|
decoder->offset_x = 0;
|
|
decoder->offset_y = 0;
|
|
decoder->rect_width = 0;
|
|
decoder->rect_height = 0;
|
|
decoder->shared_flag = TRUE;
|
|
decoder->data = NULL;
|
|
decoder->data_len = 0;
|
|
decoder->error = NULL;
|
|
|
|
g_mutex_init (&decoder->write_lock);
|
|
|
|
return decoder;
|
|
}
|
|
|
|
void
|
|
rfb_decoder_free (RfbDecoder * decoder)
|
|
{
|
|
g_return_if_fail (decoder != NULL);
|
|
|
|
rfb_decoder_disconnect (decoder);
|
|
|
|
g_clear_object (&decoder->socket_client);
|
|
g_clear_object (&decoder->cancellable);
|
|
g_mutex_clear (&decoder->write_lock);
|
|
g_free (decoder);
|
|
}
|
|
|
|
gboolean
|
|
rfb_decoder_connect_tcp (RfbDecoder * decoder, gchar * host, guint port)
|
|
{
|
|
GError *err = NULL;
|
|
GSocketConnection *connection;
|
|
|
|
GST_DEBUG ("connecting to the rfb server");
|
|
|
|
g_return_val_if_fail (decoder != NULL, FALSE);
|
|
g_return_val_if_fail (decoder->connection == NULL, FALSE);
|
|
g_return_val_if_fail (host != NULL, FALSE);
|
|
|
|
g_cancellable_reset (decoder->cancellable);
|
|
|
|
connection =
|
|
g_socket_client_connect_to_host (decoder->socket_client, host, port,
|
|
decoder->cancellable, &err);
|
|
|
|
if (!connection)
|
|
goto connect_failed;
|
|
|
|
decoder->connection = connection;
|
|
|
|
return TRUE;
|
|
|
|
connect_failed:
|
|
{
|
|
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GST_DEBUG ("Cancelled connecting");
|
|
} else {
|
|
GST_WARNING ("Failed to connect to host '%s:%d': %s", host, port,
|
|
err->message);
|
|
if (decoder->error == NULL) {
|
|
decoder->error = err;
|
|
err = NULL;
|
|
}
|
|
}
|
|
g_clear_error (&err);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
rfb_decoder_disconnect (RfbDecoder * decoder)
|
|
{
|
|
GST_DEBUG ("Disconnecting from the rfb server");
|
|
|
|
g_return_if_fail (decoder);
|
|
g_return_if_fail (decoder->cancellable);
|
|
|
|
g_cancellable_cancel (decoder->cancellable);
|
|
|
|
/* Make sure threaded write a done first, this avoids race condition,
|
|
* specially when the decoder is freed */
|
|
g_mutex_lock (&decoder->write_lock);
|
|
|
|
g_clear_object (&decoder->connection);
|
|
g_clear_error (&decoder->error);
|
|
g_clear_pointer (&decoder->data, g_free);
|
|
|
|
g_mutex_unlock (&decoder->write_lock);
|
|
}
|
|
|
|
/**
|
|
* rfb_decoder_iterate:
|
|
* @decoder: The rfb context
|
|
*
|
|
* Initializes the connection with the rfb server
|
|
*
|
|
* Returns: TRUE if initialization was succesfull, FALSE on fail.
|
|
*/
|
|
gboolean
|
|
rfb_decoder_iterate (RfbDecoder * decoder)
|
|
{
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (decoder != NULL, FALSE);
|
|
g_return_val_if_fail (decoder->connection != NULL, FALSE);
|
|
|
|
if (decoder->state == NULL) {
|
|
GST_DEBUG ("First iteration: set state to -> wait for protocol version");
|
|
decoder->state = rfb_decoder_state_wait_for_protocol_version;
|
|
}
|
|
|
|
GST_DEBUG ("Executing next state in initialization");
|
|
ret = decoder->state (decoder);
|
|
|
|
if (ret == FALSE) {
|
|
if (decoder->error == NULL)
|
|
GST_WARNING ("Failure, but no error stored");
|
|
else
|
|
GST_WARNING ("Failure: %s", decoder->error->message);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static guint8 *
|
|
rfb_decoder_read (RfbDecoder * decoder, guint32 len)
|
|
{
|
|
GInputStream *in;
|
|
GError *err = NULL;
|
|
gsize count = 0;
|
|
|
|
if (!decoder->connection)
|
|
return FALSE;
|
|
|
|
g_return_val_if_fail (len > 0, NULL);
|
|
|
|
in = g_io_stream_get_input_stream (G_IO_STREAM (decoder->connection));
|
|
|
|
g_return_val_if_fail (in != NULL, NULL);
|
|
|
|
if (G_UNLIKELY (len > decoder->data_len)) {
|
|
g_free (decoder->data);
|
|
decoder->data = g_malloc (len);
|
|
decoder->data_len = len;
|
|
}
|
|
|
|
if (!g_input_stream_read_all (in, decoder->data, len, &count,
|
|
decoder->cancellable, &err))
|
|
goto recv_error;
|
|
|
|
if (count == 0) {
|
|
g_set_error_literal (&err, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE,
|
|
"Connection was closed.");
|
|
goto recv_error;
|
|
}
|
|
|
|
return decoder->data;
|
|
|
|
recv_error:
|
|
{
|
|
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GST_DEBUG ("Read on socket cancelled");
|
|
} else {
|
|
GST_ERROR ("Read error on socket: %s", err->message);
|
|
if (decoder->error == NULL) {
|
|
decoder->error = err;
|
|
err = NULL;
|
|
}
|
|
}
|
|
g_clear_error (&err);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_send (RfbDecoder * decoder, guint8 * buffer, guint len)
|
|
{
|
|
GOutputStream *out;
|
|
GError *err = NULL;
|
|
|
|
if (!decoder->connection)
|
|
return FALSE;
|
|
|
|
g_return_val_if_fail (buffer != NULL, 0);
|
|
g_return_val_if_fail (len > 0, 0);
|
|
|
|
g_mutex_lock (&decoder->write_lock);
|
|
|
|
out = g_io_stream_get_output_stream (G_IO_STREAM (decoder->connection));
|
|
|
|
if (!g_output_stream_write_all (out, buffer, len, NULL, decoder->cancellable,
|
|
&err))
|
|
goto send_error;
|
|
|
|
g_mutex_unlock (&decoder->write_lock);
|
|
|
|
return TRUE;
|
|
|
|
send_error:
|
|
{
|
|
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GST_DEBUG ("Send on socket cancelled");
|
|
} else {
|
|
GST_ERROR ("Send error on socket: %s", err->message);
|
|
if (decoder->error == NULL) {
|
|
decoder->error = err;
|
|
err = NULL;
|
|
}
|
|
}
|
|
g_clear_error (&err);
|
|
g_mutex_unlock (&decoder->write_lock);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
rfb_decoder_send_update_request (RfbDecoder * decoder,
|
|
gboolean incremental, gint x, gint y, gint width, gint height)
|
|
{
|
|
guint8 data[10];
|
|
|
|
g_return_if_fail (decoder != NULL);
|
|
g_return_if_fail (decoder->connection != NULL);
|
|
|
|
data[0] = 3;
|
|
data[1] = incremental;
|
|
RFB_SET_UINT16 (data + 2, x);
|
|
RFB_SET_UINT16 (data + 4, y);
|
|
RFB_SET_UINT16 (data + 6, width);
|
|
RFB_SET_UINT16 (data + 8, height);
|
|
|
|
rfb_decoder_send (decoder, data, 10);
|
|
|
|
/* create a backup of the prev frame for copyrect encoding */
|
|
if (decoder->use_copyrect) {
|
|
memcpy (decoder->prev_frame, decoder->frame,
|
|
decoder->rect_width * decoder->rect_height * decoder->bpp / 8);
|
|
}
|
|
|
|
decoder->state = rfb_decoder_state_normal;
|
|
}
|
|
|
|
void
|
|
rfb_decoder_send_key_event (RfbDecoder * decoder, guint key, gboolean down_flag)
|
|
{
|
|
guint8 data[8];
|
|
|
|
g_return_if_fail (decoder != NULL);
|
|
g_return_if_fail (decoder->connection != NULL);
|
|
|
|
data[0] = 4;
|
|
data[1] = down_flag;
|
|
RFB_SET_UINT16 (data + 2, 0);
|
|
RFB_SET_UINT32 (data + 4, key);
|
|
|
|
rfb_decoder_send (decoder, data, 8);
|
|
}
|
|
|
|
void
|
|
rfb_decoder_send_pointer_event (RfbDecoder * decoder,
|
|
gint button_mask, gint x, gint y)
|
|
{
|
|
guint8 data[6];
|
|
|
|
g_return_if_fail (decoder != NULL);
|
|
g_return_if_fail (decoder->connection != NULL);
|
|
|
|
data[0] = 5;
|
|
data[1] = button_mask;
|
|
RFB_SET_UINT16 (data + 2, x);
|
|
RFB_SET_UINT16 (data + 4, y);
|
|
|
|
rfb_decoder_send (decoder, data, 6);
|
|
}
|
|
|
|
/**
|
|
* rfb_decoder_state_wait_for_protocol_version:
|
|
*
|
|
* Negotiate the rfb version used
|
|
*/
|
|
static gboolean
|
|
rfb_decoder_state_wait_for_protocol_version (RfbDecoder * decoder)
|
|
{
|
|
gchar version_str[] = "RFB 003.003\n";
|
|
|
|
if (!rfb_decoder_read (decoder, 12))
|
|
return FALSE;
|
|
|
|
g_return_val_if_fail (memcmp (decoder->data, "RFB 003.", 8) == 0, FALSE);
|
|
g_return_val_if_fail (*(decoder->data + 11) == 0x0a, FALSE);
|
|
|
|
GST_DEBUG ("\"%.11s\"", decoder->data);
|
|
*(decoder->data) = 0x00;
|
|
*(decoder->data + 11) = 0x00;
|
|
decoder->protocol_major = atoi ((char *) (decoder->data + 4));
|
|
decoder->protocol_minor = atoi ((char *) (decoder->data + 8));
|
|
GST_DEBUG ("Major version : %d", decoder->protocol_major);
|
|
GST_DEBUG ("Minor version : %d", decoder->protocol_minor);
|
|
|
|
if (decoder->protocol_major != 3) {
|
|
GST_INFO
|
|
("A major protocol version of %d is not supported, falling back to 3",
|
|
decoder->protocol_major);
|
|
decoder->protocol_major = 3;
|
|
decoder->protocol_minor = 3;
|
|
}
|
|
switch (decoder->protocol_minor) {
|
|
case 3:
|
|
case 7:
|
|
case 8:
|
|
break;
|
|
default:
|
|
GST_INFO ("Minor version %d is not supported, using 3",
|
|
decoder->protocol_minor);
|
|
decoder->protocol_minor = 3;
|
|
}
|
|
|
|
version_str[10] = '0' + decoder->protocol_minor;
|
|
|
|
if (!rfb_decoder_send (decoder, (guint8 *) version_str, 12))
|
|
return FALSE;
|
|
|
|
decoder->state = rfb_decoder_state_wait_for_security;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* a string describing the reason (where a string is specified as a length
|
|
* followed by that many ASCII characters)
|
|
**/
|
|
static gboolean
|
|
rfb_decoder_state_reason (RfbDecoder * decoder)
|
|
{
|
|
gint reason_length;
|
|
|
|
if (!rfb_decoder_read (decoder, 4))
|
|
return FALSE;
|
|
|
|
reason_length = RFB_GET_UINT32 (decoder->data);
|
|
|
|
if (!rfb_decoder_read (decoder, reason_length))
|
|
return FALSE;
|
|
|
|
GST_WARNING ("Reason by server: %s", decoder->data);
|
|
|
|
if (decoder->error == NULL) {
|
|
decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
|
|
"VNC server error: %s", decoder->data);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_wait_for_security (RfbDecoder * decoder)
|
|
{
|
|
/*
|
|
* Version 3.3 The server decides the security type and sends a single word
|
|
*
|
|
* The security-type may only take the value 0, 1 or 2. A value of 0 means that the
|
|
* connection has failed and is followed by a string giving the reason, as described
|
|
* above.
|
|
*/
|
|
if (IS_VERSION_3_3 (decoder)) {
|
|
if (!rfb_decoder_read (decoder, 4))
|
|
return FALSE;
|
|
|
|
decoder->security_type = RFB_GET_UINT32 (decoder->data);
|
|
GST_DEBUG ("security = %d", decoder->security_type);
|
|
|
|
g_return_val_if_fail (decoder->security_type < 3, FALSE);
|
|
|
|
if (decoder->security_type == SECURITY_FAIL) {
|
|
decoder->state = rfb_decoder_state_reason;
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
guint8 num_type;
|
|
gint i;
|
|
guint8 *type = NULL;
|
|
|
|
if (!rfb_decoder_read (decoder, 1))
|
|
return FALSE;
|
|
|
|
num_type = RFB_GET_UINT8 (decoder->data);
|
|
if (num_type == 0) {
|
|
decoder->state = rfb_decoder_state_reason;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!rfb_decoder_read (decoder, num_type))
|
|
return FALSE;
|
|
|
|
decoder->security_type = SECURITY_FAIL;
|
|
|
|
/* For now, simply pick the first support security method */
|
|
for (i = 0; i < num_type; i++) {
|
|
guint val = RFB_GET_UINT8 (decoder->data + i);
|
|
|
|
GST_DEBUG ("Server supports security type %u", val);
|
|
|
|
if (val == SECURITY_NONE || val == SECURITY_VNC) {
|
|
decoder->security_type = val;
|
|
type = decoder->data + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!type) {
|
|
GST_WARNING ("Security type negotiation failed.");
|
|
decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
|
|
"VNC server requires unsupported security method.");
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG ("security = %d", decoder->security_type);
|
|
if (!rfb_decoder_send (decoder, type, 1))
|
|
return FALSE;
|
|
}
|
|
|
|
switch (decoder->security_type) {
|
|
case SECURITY_NONE:
|
|
GST_DEBUG ("Security type is None");
|
|
if (IS_VERSION_3_8 (decoder)) {
|
|
decoder->state = rfb_decoder_state_security_result;
|
|
} else {
|
|
decoder->state = rfb_decoder_state_send_client_initialisation;
|
|
}
|
|
break;
|
|
case SECURITY_VNC:{
|
|
unsigned char key[8], *challenge;
|
|
DESContext des_ctx;
|
|
gsize password_len;
|
|
|
|
/*
|
|
* VNC authentication is to be used and protocol data is to be sent
|
|
* unencrypted. The server sends a random 16-byte challenge
|
|
*/
|
|
GST_DEBUG ("Security type is VNC Authentication");
|
|
/* VNC Authentication can't be used if the password is not set */
|
|
if (!decoder->password) {
|
|
GST_WARNING
|
|
("VNC Authentication can't be used if the password is not set");
|
|
decoder->error =
|
|
g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
|
|
"VNC servers needs authentication, but no password set");
|
|
return FALSE;
|
|
}
|
|
|
|
/* key is 8 bytes and made up of password (padded with 0s if needed) */
|
|
memset (key, 0, 8);
|
|
password_len = strlen (decoder->password);
|
|
memcpy (key, decoder->password, MIN (password_len, 8));
|
|
|
|
/* read challenge */
|
|
challenge = rfb_decoder_read (decoder, 16);
|
|
if (challenge == NULL)
|
|
return FALSE;
|
|
|
|
/* encrypt 16 challenge bytes in place using key */
|
|
memset (&des_ctx, 0, sizeof (DESContext));
|
|
deskey (&des_ctx, key, EN0);
|
|
des (&des_ctx, challenge, challenge);
|
|
des (&des_ctx, challenge + 8, challenge + 8);
|
|
|
|
/* .. and send back to server */
|
|
if (!rfb_decoder_send (decoder, challenge, 16))
|
|
return FALSE;
|
|
|
|
GST_DEBUG ("Encrypted challenge sent to server");
|
|
|
|
decoder->state = rfb_decoder_state_security_result;
|
|
break;
|
|
}
|
|
default:
|
|
GST_WARNING ("Security type is not known");
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* The server sends a word to inform the client whether the security handshaking was
|
|
* successful.
|
|
*/
|
|
static gboolean
|
|
rfb_decoder_state_security_result (RfbDecoder * decoder)
|
|
{
|
|
if (!rfb_decoder_read (decoder, 4))
|
|
return FALSE;
|
|
|
|
if (RFB_GET_UINT32 (decoder->data) != 0) {
|
|
GST_WARNING ("Security handshaking failed");
|
|
if (IS_VERSION_3_8 (decoder)) {
|
|
decoder->state = rfb_decoder_state_reason;
|
|
return TRUE;
|
|
}
|
|
if (decoder->error == NULL) {
|
|
decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
|
|
"authentication failed");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG ("Security handshaking succesful");
|
|
decoder->state = rfb_decoder_state_send_client_initialisation;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static guint8 *
|
|
rfb_decoder_message_set_encodings (GSList * encodings_list)
|
|
{
|
|
|
|
guint8 *message = g_malloc0 (4 + 4 * g_slist_length (encodings_list));
|
|
guint32 *encoding_type;
|
|
|
|
message[0] = 0x02; /* message type */
|
|
RFB_SET_UINT16 (message + 2, g_slist_length (encodings_list)); /* number of encodings */
|
|
|
|
/* write all the encoding types */
|
|
encoding_type = (guint32 *) (message + 4);
|
|
|
|
while (encodings_list) {
|
|
RFB_SET_UINT32 (encoding_type, GPOINTER_TO_UINT (encodings_list->data));
|
|
encoding_type++;
|
|
encodings_list = encodings_list->next;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
/*
|
|
* rfb_decoder_state_set_encodings:
|
|
* @decoder: The rfb context
|
|
*
|
|
* Sends the encoding types that the client can decode to the server
|
|
*
|
|
* Returns: TRUE if initialization was succesfull, FALSE on fail.
|
|
*/
|
|
static gboolean
|
|
rfb_decoder_state_set_encodings (RfbDecoder * decoder)
|
|
{
|
|
GSList *encoder_list = NULL;
|
|
guint8 *message;
|
|
|
|
GST_DEBUG ("entered set encodings");
|
|
|
|
encoder_list =
|
|
g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_HEXTILE));
|
|
encoder_list =
|
|
g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_CORRE));
|
|
encoder_list =
|
|
g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RRE));
|
|
if (decoder->use_copyrect) {
|
|
encoder_list =
|
|
g_slist_append (encoder_list,
|
|
GUINT_TO_POINTER (ENCODING_TYPE_COPYRECT));
|
|
}
|
|
encoder_list =
|
|
g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RAW));
|
|
|
|
message = rfb_decoder_message_set_encodings (encoder_list);
|
|
|
|
if (!rfb_decoder_send (decoder, message,
|
|
4 + 4 * g_slist_length (encoder_list))) {
|
|
g_free (message);
|
|
return FALSE;
|
|
}
|
|
|
|
g_free (message);
|
|
|
|
decoder->state = rfb_decoder_state_normal;
|
|
decoder->inited = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_send_client_initialisation (RfbDecoder * decoder)
|
|
{
|
|
guint8 shared_flag;
|
|
|
|
shared_flag = decoder->shared_flag;
|
|
|
|
if (!rfb_decoder_send (decoder, &shared_flag, 1))
|
|
return FALSE;
|
|
|
|
GST_DEBUG ("shared_flag is %d", shared_flag);
|
|
|
|
decoder->state = rfb_decoder_state_wait_for_server_initialisation;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_wait_for_server_initialisation (RfbDecoder * decoder)
|
|
{
|
|
guint32 name_length;
|
|
|
|
if (!rfb_decoder_read (decoder, 24))
|
|
return FALSE;
|
|
|
|
decoder->width = RFB_GET_UINT16 (decoder->data + 0);
|
|
decoder->height = RFB_GET_UINT16 (decoder->data + 2);
|
|
decoder->bpp = RFB_GET_UINT8 (decoder->data + 4);
|
|
decoder->depth = RFB_GET_UINT8 (decoder->data + 5);
|
|
decoder->big_endian = RFB_GET_UINT8 (decoder->data + 6);
|
|
decoder->true_colour = RFB_GET_UINT8 (decoder->data + 7);
|
|
decoder->red_max = RFB_GET_UINT16 (decoder->data + 8);
|
|
decoder->green_max = RFB_GET_UINT16 (decoder->data + 10);
|
|
decoder->blue_max = RFB_GET_UINT16 (decoder->data + 12);
|
|
decoder->red_shift = RFB_GET_UINT8 (decoder->data + 14);
|
|
decoder->green_shift = RFB_GET_UINT8 (decoder->data + 15);
|
|
decoder->blue_shift = RFB_GET_UINT8 (decoder->data + 16);
|
|
|
|
GST_DEBUG ("Server Initialization");
|
|
GST_DEBUG ("width = %d", decoder->width);
|
|
GST_DEBUG ("height = %d", decoder->height);
|
|
GST_DEBUG ("bpp = %d", decoder->bpp);
|
|
GST_DEBUG ("depth = %d", decoder->depth);
|
|
GST_DEBUG ("big_endian = %d", decoder->big_endian);
|
|
GST_DEBUG ("true_colour= %d", decoder->true_colour);
|
|
GST_DEBUG ("red_max = %d", decoder->red_max);
|
|
GST_DEBUG ("green_max = %d", decoder->green_max);
|
|
GST_DEBUG ("blue_max = %d", decoder->blue_max);
|
|
GST_DEBUG ("red_shift = %d", decoder->red_shift);
|
|
GST_DEBUG ("green_shift= %d", decoder->green_shift);
|
|
GST_DEBUG ("blue_shift = %d", decoder->blue_shift);
|
|
|
|
name_length = RFB_GET_UINT32 (decoder->data + 20);
|
|
|
|
if (!rfb_decoder_read (decoder, name_length))
|
|
return FALSE;
|
|
|
|
decoder->name = g_strndup ((gchar *) (decoder->data), name_length);
|
|
GST_DEBUG ("name = %s", decoder->name);
|
|
|
|
/* check if we need cropping */
|
|
|
|
if (decoder->offset_x > 0) {
|
|
if (decoder->offset_x > decoder->width) {
|
|
GST_WARNING
|
|
("Trying to crop more than the width of the server. Setting offset-x to 0.");
|
|
decoder->offset_x = 0;
|
|
} else {
|
|
decoder->width -= decoder->offset_x;
|
|
}
|
|
}
|
|
if (decoder->offset_y > 0) {
|
|
if (decoder->offset_y > decoder->height) {
|
|
GST_WARNING
|
|
("Trying to crop more than the height of the server. Setting offset-y to 0.");
|
|
decoder->offset_y = 0;
|
|
} else {
|
|
decoder->height -= decoder->offset_y;
|
|
}
|
|
}
|
|
if (decoder->rect_width > 0) {
|
|
if (decoder->rect_width > decoder->width) {
|
|
GST_WARNING
|
|
("Trying to crop more than the width of the server. Setting width to %u.",
|
|
decoder->width);
|
|
decoder->rect_width = decoder->width;
|
|
} else {
|
|
decoder->width = decoder->rect_width;
|
|
}
|
|
}
|
|
if (decoder->rect_height > 0) {
|
|
if (decoder->rect_height > decoder->height) {
|
|
GST_WARNING
|
|
("Trying to crop more than the height of the server. Setting height to %u.",
|
|
decoder->height);
|
|
decoder->rect_height = decoder->height;
|
|
} else {
|
|
decoder->height = decoder->rect_height;
|
|
}
|
|
}
|
|
|
|
decoder->state = rfb_decoder_state_set_encodings;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_normal (RfbDecoder * decoder)
|
|
{
|
|
gint message_type;
|
|
|
|
GST_DEBUG ("decoder_state_normal");
|
|
|
|
if (!rfb_decoder_read (decoder, 1))
|
|
return FALSE;
|
|
|
|
message_type = RFB_GET_UINT8 (decoder->data);
|
|
|
|
switch (message_type) {
|
|
case MESSAGE_TYPE_FRAMEBUFFER_UPDATE:
|
|
GST_DEBUG ("Receiving framebuffer update");
|
|
decoder->state = rfb_decoder_state_framebuffer_update;
|
|
break;
|
|
case 1:
|
|
decoder->state = rfb_decoder_state_set_colour_map_entries;
|
|
break;
|
|
case 2:
|
|
/* bell, ignored */
|
|
decoder->state = rfb_decoder_state_normal;
|
|
break;
|
|
case 3:
|
|
decoder->state = rfb_decoder_state_server_cut_text;
|
|
break;
|
|
default:
|
|
g_critical ("unknown message type %d", message_type);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_framebuffer_update (RfbDecoder * decoder)
|
|
{
|
|
|
|
if (!rfb_decoder_read (decoder, 3))
|
|
return FALSE;
|
|
|
|
decoder->n_rects = RFB_GET_UINT16 (decoder->data + 1);
|
|
GST_DEBUG ("Number of rectangles : %d", decoder->n_rects);
|
|
|
|
decoder->state = rfb_decoder_state_framebuffer_update_rectangle;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder * decoder)
|
|
{
|
|
gint x, y, w, h;
|
|
gint encoding;
|
|
gboolean ret = FALSE;
|
|
|
|
if (!rfb_decoder_read (decoder, 12))
|
|
return FALSE;
|
|
|
|
x = RFB_GET_UINT16 (decoder->data + 0) - decoder->offset_x;
|
|
y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y;
|
|
w = RFB_GET_UINT16 (decoder->data + 4);
|
|
h = RFB_GET_UINT16 (decoder->data + 6);
|
|
encoding = RFB_GET_UINT32 (decoder->data + 8);
|
|
|
|
GST_DEBUG ("update recieved");
|
|
GST_DEBUG ("x:%d y:%d", x, y);
|
|
GST_DEBUG ("w:%d h:%d", w, h);
|
|
GST_DEBUG ("encoding: %d", encoding);
|
|
|
|
if (((w * h) + (x * y)) > (decoder->width * decoder->height)) {
|
|
GST_ERROR ("Desktop resize is unsupported.");
|
|
decoder->state = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
switch (encoding) {
|
|
case ENCODING_TYPE_RAW:
|
|
ret = rfb_decoder_raw_encoding (decoder, x, y, w, h);
|
|
break;
|
|
case ENCODING_TYPE_COPYRECT:
|
|
ret = rfb_decoder_copyrect_encoding (decoder, x, y, w, h);
|
|
break;
|
|
case ENCODING_TYPE_RRE:
|
|
ret = rfb_decoder_rre_encoding (decoder, x, y, w, h);
|
|
break;
|
|
case ENCODING_TYPE_CORRE:
|
|
ret = rfb_decoder_corre_encoding (decoder, x, y, w, h);
|
|
break;
|
|
case ENCODING_TYPE_HEXTILE:
|
|
ret = rfb_decoder_hextile_encoding (decoder, x, y, w, h);
|
|
break;
|
|
default:
|
|
g_critical ("unimplemented encoding\n");
|
|
break;
|
|
}
|
|
|
|
if (!ret)
|
|
return FALSE;
|
|
|
|
decoder->n_rects--;
|
|
if (decoder->n_rects == 0) {
|
|
decoder->state = NULL;
|
|
} else {
|
|
decoder->state = rfb_decoder_state_framebuffer_update_rectangle;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
|
|
gint rect_w, gint rect_h)
|
|
{
|
|
gint size;
|
|
guint8 *frame, *p;
|
|
guint32 raw_line_size;
|
|
|
|
raw_line_size = rect_w * decoder->bytespp;
|
|
size = rect_h * raw_line_size;
|
|
|
|
GST_DEBUG ("Reading %d bytes (%dx%d)", size, rect_w, rect_h);
|
|
|
|
if (!rfb_decoder_read (decoder, size))
|
|
return FALSE;
|
|
|
|
frame =
|
|
decoder->frame + (((start_y * decoder->rect_width) +
|
|
start_x) * decoder->bytespp);
|
|
p = decoder->data;
|
|
|
|
while (rect_h--) {
|
|
memcpy (frame, p, raw_line_size);
|
|
p += raw_line_size;
|
|
frame += decoder->line_size;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_copyrect_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
|
|
gint rect_w, gint rect_h)
|
|
{
|
|
guint16 src_x, src_y;
|
|
gint line_width, copyrect_width;
|
|
guint8 *src, *dst;
|
|
|
|
if (!rfb_decoder_read (decoder, 4))
|
|
return FALSE;
|
|
|
|
/* don't forget the offset */
|
|
src_x = RFB_GET_UINT16 (decoder->data) - decoder->offset_x;
|
|
src_y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y;
|
|
GST_DEBUG ("Copyrect from %d %d", src_x, src_y);
|
|
|
|
copyrect_width = rect_w * decoder->bytespp;
|
|
line_width = decoder->line_size;
|
|
src =
|
|
decoder->prev_frame + ((src_y * decoder->rect_width) +
|
|
src_x) * decoder->bytespp;
|
|
dst =
|
|
decoder->frame + ((start_y * decoder->rect_width) +
|
|
start_x) * decoder->bytespp;
|
|
|
|
while (rect_h--) {
|
|
memcpy (dst, src, copyrect_width);
|
|
src += line_width;
|
|
dst += line_width;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
rfb_decoder_fill_rectangle (RfbDecoder * decoder, gint x, gint y, gint w,
|
|
gint h, guint32 color)
|
|
{
|
|
/* fill the whole region with the same color */
|
|
|
|
guint32 *offset;
|
|
gint i, j;
|
|
|
|
for (i = 0; i < h; i++) {
|
|
offset =
|
|
(guint32 *) (decoder->frame + ((x + (y +
|
|
i) * decoder->rect_width)) * decoder->bytespp);
|
|
for (j = 0; j < w; j++) {
|
|
*(offset++) = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
|
|
gint rect_w, gint rect_h)
|
|
{
|
|
guint32 number_of_rectangles, color;
|
|
guint16 x, y, w, h;
|
|
|
|
if (!rfb_decoder_read (decoder, 4 + decoder->bytespp))
|
|
return FALSE;
|
|
|
|
number_of_rectangles = RFB_GET_UINT32 (decoder->data);
|
|
color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4)));
|
|
|
|
GST_DEBUG ("number of rectangles :%d", number_of_rectangles);
|
|
|
|
/* color the background of this rectangle */
|
|
rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color);
|
|
|
|
while (number_of_rectangles--) {
|
|
|
|
if (!rfb_decoder_read (decoder, decoder->bytespp + 8))
|
|
return FALSE;
|
|
|
|
color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
|
|
x = RFB_GET_UINT16 (decoder->data + decoder->bytespp);
|
|
y = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 2);
|
|
w = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 4);
|
|
h = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 6);
|
|
|
|
/* draw the rectangle in the foreground */
|
|
rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
|
|
gint rect_w, gint rect_h)
|
|
{
|
|
guint32 number_of_rectangles, color;
|
|
guint8 x, y, w, h;
|
|
|
|
if (!rfb_decoder_read (decoder, 4 + decoder->bytespp))
|
|
return FALSE;
|
|
|
|
number_of_rectangles = RFB_GET_UINT32 (decoder->data);
|
|
color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4)));
|
|
g_free (decoder->data);
|
|
|
|
GST_DEBUG ("number of rectangles :%d", number_of_rectangles);
|
|
|
|
/* color the background of this rectangle */
|
|
rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color);
|
|
|
|
while (number_of_rectangles--) {
|
|
|
|
if (!rfb_decoder_read (decoder, decoder->bytespp + 4))
|
|
return FALSE;
|
|
|
|
color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
|
|
x = RFB_GET_UINT8 (decoder->data + decoder->bytespp);
|
|
y = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 1);
|
|
w = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 2);
|
|
h = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 3);
|
|
|
|
/* draw the rectangle in the foreground */
|
|
rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color);
|
|
|
|
g_free (decoder->data);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_hextile_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
|
|
gint rect_w, gint rect_h)
|
|
{
|
|
gint32 x, x_count G_GNUC_UNUSED, x_end, x_max, x_max_16;
|
|
gint32 y, y_count G_GNUC_UNUSED, y_end, y_max, y_max_16;
|
|
guint8 subencoding, nr_subrect, xy, wh;
|
|
guint32 background, foreground;
|
|
|
|
foreground = background = 0;
|
|
x_end = rect_w % 16;
|
|
x_count = rect_w / 16 + (x_end > 0 ? 1 : 0);
|
|
y_end = rect_h % 16;
|
|
y_count = rect_h / 16 + (y_end > 0 ? 1 : 0);
|
|
x_max = start_x + rect_w;
|
|
y_max = start_y + rect_h;
|
|
x_max_16 = x_max - 16;
|
|
y_max_16 = y_max - 16;
|
|
|
|
for (y = start_y; y < y_max; y += 16) {
|
|
for (x = start_x; x < x_max; x += 16) {
|
|
|
|
if (!rfb_decoder_read (decoder, 1))
|
|
return FALSE;
|
|
|
|
subencoding = RFB_GET_UINT8 (decoder->data);
|
|
|
|
if (subencoding & SUBENCODING_RAW) {
|
|
rfb_decoder_raw_encoding (decoder, x, y,
|
|
(x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end));
|
|
continue;
|
|
}
|
|
|
|
if (subencoding & SUBENCODING_BACKGROUND) {
|
|
if (!rfb_decoder_read (decoder, decoder->bytespp))
|
|
return FALSE;
|
|
|
|
background = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
|
|
}
|
|
rfb_decoder_fill_rectangle (decoder, x, y,
|
|
(x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end),
|
|
background);
|
|
|
|
if (subencoding & SUBENCODING_FOREGROUND) {
|
|
if (!rfb_decoder_read (decoder, decoder->bytespp))
|
|
return FALSE;
|
|
|
|
foreground = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
|
|
}
|
|
|
|
if (subencoding & SUBENCODING_ANYSUBRECTS) {
|
|
if (!rfb_decoder_read (decoder, 1))
|
|
return FALSE;
|
|
|
|
nr_subrect = RFB_GET_UINT8 (decoder->data);
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (subencoding & SUBENCODING_SUBRECTSCOLORED) {
|
|
guint offset = 0;
|
|
|
|
if (!rfb_decoder_read (decoder, nr_subrect * (2 + decoder->bytespp)))
|
|
return FALSE;
|
|
|
|
while (nr_subrect--) {
|
|
foreground =
|
|
GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + offset)));
|
|
offset += decoder->bytespp;
|
|
xy = RFB_GET_UINT8 (decoder->data + offset++);
|
|
wh = RFB_GET_UINT8 (decoder->data + offset++);
|
|
rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF),
|
|
1 + (wh >> 4), 1 + (wh & 0xF), foreground);
|
|
}
|
|
} else {
|
|
guint offset = 0;
|
|
|
|
if (!rfb_decoder_read (decoder, 2 * nr_subrect))
|
|
return FALSE;
|
|
|
|
while (nr_subrect--) {
|
|
xy = RFB_GET_UINT8 (decoder->data + offset++);
|
|
wh = RFB_GET_UINT8 (decoder->data + offset++);
|
|
rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF),
|
|
1 + (wh >> 4), 1 + (wh & 0xF), foreground);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder)
|
|
{
|
|
g_critical ("not implemented");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
rfb_decoder_state_server_cut_text (RfbDecoder * decoder)
|
|
{
|
|
gint cut_text_length;
|
|
|
|
/* 3 bytes padding, 4 bytes cut_text_length */
|
|
if (!rfb_decoder_read (decoder, 7))
|
|
return FALSE;
|
|
|
|
cut_text_length = RFB_GET_UINT32 (decoder->data + 3);
|
|
|
|
if (!rfb_decoder_read (decoder, cut_text_length))
|
|
return FALSE;
|
|
|
|
GST_DEBUG ("rfb_decoder_state_server_cut_text: throw away '%s'",
|
|
decoder->data);
|
|
|
|
decoder->state = rfb_decoder_state_normal;
|
|
|
|
return TRUE;
|
|
}
|