rdesktop/seamless.c
Pierre Ossman 25b8412333 Avoid poking around in STREAM internals
It's easy to make mistakes this way, and bypassed the normal bounds
checking. So make sure we always use macros or functions.
2019-05-06 14:33:38 +02:00

552 lines
10 KiB
C

/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Seamless Windows support
Copyright 2005-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
Copyright 2013-2017 Henrik Andersson <hean01@cendio.se> for Cendio AB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rdesktop.h"
#include <stdarg.h>
#include <assert.h>
extern RD_BOOL g_seamless_rdp;
static VCHANNEL *seamless_channel;
static unsigned int seamless_serial;
static char *seamless_rest = NULL;
static char icon_buf[1024];
static char *
seamless_get_token(char **s)
{
char *comma, *head;
head = *s;
if (!head)
return NULL;
comma = strchr(head, ',');
if (comma)
{
*comma = '\0';
*s = comma + 1;
}
else
{
*s = NULL;
}
return head;
}
static RD_BOOL
seamless_process_line(const char *line, void *data)
{
UNUSED(data);
char *p, *l;
char *tok1, *tok3, *tok4, *tok5, *tok6, *tok7, *tok8;
unsigned long id, flags;
char *endptr;
l = xstrdup(line);
p = l;
logger(Core, Debug, "seamless_process_line(), got '%s'", p);
tok1 = seamless_get_token(&p);
(void) seamless_get_token(&p);
tok3 = seamless_get_token(&p);
tok4 = seamless_get_token(&p);
tok5 = seamless_get_token(&p);
tok6 = seamless_get_token(&p);
tok7 = seamless_get_token(&p);
tok8 = seamless_get_token(&p);
if (!strcmp("CREATE", tok1))
{
unsigned long group, parent;
if (!tok6)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
group = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
parent = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok6, &endptr, 0);
if (*endptr)
return False;
ui_seamless_create_window(id, group, parent, flags);
}
else if (!strcmp("DESTROY", tok1))
{
if (!tok4)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
ui_seamless_destroy_window(id, flags);
}
else if (!strcmp("DESTROYGRP", tok1))
{
if (!tok4)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
ui_seamless_destroy_group(id, flags);
}
else if (!strcmp("SETICON", tok1))
{
int chunk, width, height, len;
char byte[3];
if (!tok8)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
chunk = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
width = strtoul(tok6, &endptr, 0);
if (*endptr)
return False;
height = strtoul(tok7, &endptr, 0);
if (*endptr)
return False;
byte[2] = '\0';
len = 0;
while (*tok8 != '\0')
{
byte[0] = *tok8;
tok8++;
if (*tok8 == '\0')
return False;
byte[1] = *tok8;
tok8++;
icon_buf[len] = strtol(byte, NULL, 16);
len++;
if ((size_t)len >= sizeof(icon_buf))
{
logger(Protocol, Warning, "seamless_process_line(), icon data would overrun icon_buf");
break;
}
}
ui_seamless_seticon(id, tok5, width, height, chunk, icon_buf, len);
}
else if (!strcmp("DELICON", tok1))
{
int width, height;
if (!tok6)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
width = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
height = strtoul(tok6, &endptr, 0);
if (*endptr)
return False;
ui_seamless_delicon(id, tok4, width, height);
}
else if (!strcmp("POSITION", tok1))
{
int x, y, width, height;
if (!tok8)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
x = strtol(tok4, &endptr, 0);
if (*endptr)
return False;
y = strtol(tok5, &endptr, 0);
if (*endptr)
return False;
width = strtol(tok6, &endptr, 0);
if (*endptr)
return False;
height = strtol(tok7, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok8, &endptr, 0);
if (*endptr)
return False;
ui_seamless_move_window(id, x, y, width, height, flags);
}
else if (!strcmp("ZCHANGE", tok1))
{
unsigned long behind;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
behind = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_restack_window(id, behind, flags);
}
else if (!strcmp("TITLE", tok1))
{
if (!tok5)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_settitle(id, tok4, flags);
}
else if (!strcmp("STATE", tok1))
{
unsigned int state;
if (!tok5)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
state = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_setstate(id, state, flags);
}
else if (!strcmp("DEBUG", tok1))
{
logger(Core, Debug, "seamless_process_line(), %s", line);
}
else if (!strcmp("SYNCBEGIN", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_syncbegin(flags);
}
else if (!strcmp("SYNCEND", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
/* do nothing, currently */
}
else if (!strcmp("HELLO", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_begin(! !(flags & SEAMLESSRDP_HELLO_HIDDEN));
}
else if (!strcmp("ACK", tok1))
{
unsigned int serial;
serial = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_ack(serial);
}
else if (!strcmp("HIDE", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_hide_desktop();
}
else if (!strcmp("UNHIDE", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_unhide_desktop();
}
xfree(l);
return True;
}
static RD_BOOL
seamless_line_handler(const char *line, void *data)
{
if (!seamless_process_line(line, data))
{
logger(Core, Warning, "seamless_line_handler(), invalid request '%s'", line);
}
return True;
}
static void
seamless_process(STREAM s)
{
unsigned int pkglen;
char *buf;
pkglen = s_remaining(s);
/* str_handle_lines requires null terminated strings */
buf = xmalloc(pkglen + 1);
in_uint8a(s, buf, pkglen);
buf[pkglen] = '\0';
str_handle_lines(buf, &seamless_rest, seamless_line_handler, NULL);
xfree(buf);
}
RD_BOOL
seamless_init(void)
{
if (!g_seamless_rdp)
return False;
seamless_serial = 0;
seamless_channel =
channel_register("seamrdp", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
seamless_process);
return (seamless_channel != NULL);
}
void
seamless_reset_state(void)
{
if (seamless_rest != NULL)
{
xfree(seamless_rest);
seamless_rest = NULL;
}
}
static unsigned int
seamless_send(const char *command, const char *format, ...)
{
STREAM s;
size_t len;
va_list argp;
char *escaped, buf[1025];
len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, seamless_serial);
assert(len < (sizeof(buf) - 1));
va_start(argp, format);
len += vsnprintf(buf + len, sizeof(buf) - len - 1, format, argp);
va_end(argp);
assert(len < (sizeof(buf) - 1));
escaped = utils_string_escape(buf);
len = snprintf(buf, sizeof(buf), "%s", escaped);
free(escaped);
assert(len < (sizeof(buf) - 1));
buf[len] = '\n';
buf[len + 1] = '\0';
len++;
s = channel_init(seamless_channel, len);
out_uint8a(s, buf, len) s_mark_end(s);
logger(Core, Debug, "seamless_send(), sending '%s'", buf);
channel_send(s, seamless_channel);
s_free(s);
return seamless_serial++;
}
unsigned int
seamless_send_sync()
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("SYNC", "");
}
unsigned int
seamless_send_state(unsigned long id, unsigned int state, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("STATE", "0x%08lx,0x%x,0x%lx", id, state, flags);
}
unsigned int
seamless_send_position(unsigned long id, int x, int y, int width, int height, unsigned long flags)
{
return seamless_send("POSITION", "0x%08lx,%d,%d,%d,%d,0x%lx", id, x, y, width, height,
flags);
}
/* Update select timeout */
void
seamless_select_timeout(struct timeval *tv)
{
struct timeval ourtimeout = { 0, SEAMLESSRDP_POSITION_TIMER };
if (g_seamless_rdp)
{
if (timercmp(&ourtimeout, tv, <))
{
tv->tv_sec = ourtimeout.tv_sec;
tv->tv_usec = ourtimeout.tv_usec;
}
}
}
unsigned int
seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("ZCHANGE", "0x%08lx,0x%08lx,0x%lx", id, below, flags);
}
unsigned int
seamless_send_focus(unsigned long id, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("FOCUS", "0x%08lx,0x%lx", id, flags);
}
/* Send client-to-server message to destroy process on the server. */
unsigned int
seamless_send_destroy(unsigned long id)
{
return seamless_send("DESTROY", "0x%08lx", id);
}
unsigned int
seamless_send_spawn(char *cmdline)
{
unsigned int res;
if (!g_seamless_rdp)
return (unsigned int) -1;
res = seamless_send("SPAWN", cmdline);
return res;
}
unsigned int
seamless_send_persistent(RD_BOOL enable)
{
unsigned int res;
if (!g_seamless_rdp)
return (unsigned int) -1;
logger(Core, Debug, "seamless_send_persistent(), %s persistent seamless mode",
enable ? "enable" : "disable");
res = seamless_send("PERSISTENT", "%d", enable);
return res;
}