Intitial implementation of seamless connection sharing:
- Implementation of ctrl functionality were slaves can call a method into existing master process, more information is found in doc/ctrl.txt - Implementation of new seamless SPAWN functionality so which is used by the ctrl to spawn processes in a seamless rdp session out of process. git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/rdesktop/trunk@1689 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
parent
15e4518b13
commit
be93803129
@ -28,8 +28,8 @@ SOUNDOBJ = @SOUNDOBJ@
|
|||||||
SCARDOBJ = @SCARDOBJ@
|
SCARDOBJ = @SCARDOBJ@
|
||||||
CREDSSPOBJ = @CREDSSPOBJ@
|
CREDSSPOBJ = @CREDSSPOBJ@
|
||||||
|
|
||||||
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o
|
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o utils.o
|
||||||
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o
|
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o ctrl.o
|
||||||
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
|
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
518
ctrl.c
Normal file
518
ctrl.c
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
/* -*- c-basic-offset: 8 -*-
|
||||||
|
rdesktop: A Remote Desktop Protocol client.
|
||||||
|
Master/Slave remote controlling
|
||||||
|
Copyright 2013 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 "ssl.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define CTRL_LINEBUF_SIZE 1024
|
||||||
|
#define CTRL_RESULT_SIZE 32
|
||||||
|
#define RDESKTOP_CTRLSOCK_STORE "/.local/var/run/rdesktop"
|
||||||
|
|
||||||
|
#define CTRL_HASH_FLAG_SEAMLESS 1
|
||||||
|
|
||||||
|
#define ERR_RESULT_OK 0x00
|
||||||
|
#define ERR_RESULT_NO_SUCH_COMMAND 0xffffffff
|
||||||
|
|
||||||
|
extern RD_BOOL g_seamless_rdp;
|
||||||
|
extern uint8 g_static_rdesktop_salt_16[];
|
||||||
|
|
||||||
|
static RD_BOOL _ctrl_is_slave;
|
||||||
|
static int ctrlsock;
|
||||||
|
static char ctrlsock_name[PATH_MAX];
|
||||||
|
static struct _ctrl_slave_t *_ctrl_slaves;
|
||||||
|
|
||||||
|
#define CMD_SEAMLESS_SPAWN "seamless.spawn"
|
||||||
|
|
||||||
|
typedef struct _ctrl_slave_t
|
||||||
|
{
|
||||||
|
struct _ctrl_slave_t *prev, *next;
|
||||||
|
int sock;
|
||||||
|
char linebuf[CTRL_LINEBUF_SIZE];
|
||||||
|
} _ctrl_slave_t;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ctrl_slave_new(int sock)
|
||||||
|
{
|
||||||
|
_ctrl_slave_t *it, *ns;
|
||||||
|
|
||||||
|
/* initialize new slave list item */
|
||||||
|
ns = (_ctrl_slave_t *) xmalloc(sizeof(_ctrl_slave_t));
|
||||||
|
memset(ns, 0, sizeof(_ctrl_slave_t));
|
||||||
|
ns->sock = sock;
|
||||||
|
|
||||||
|
/* append new slave to end of list */
|
||||||
|
it = _ctrl_slaves;
|
||||||
|
|
||||||
|
/* find last element in list */
|
||||||
|
while (it && it->next)
|
||||||
|
it = it->next;
|
||||||
|
|
||||||
|
/* if last found append new */
|
||||||
|
if (it)
|
||||||
|
{
|
||||||
|
it->next = ns;
|
||||||
|
ns->prev = it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no elemnts in list, lets add first */
|
||||||
|
_ctrl_slaves = ns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ctrl_slave_disconnect(int sock)
|
||||||
|
{
|
||||||
|
_ctrl_slave_t *it;
|
||||||
|
|
||||||
|
if (!_ctrl_slaves)
|
||||||
|
return;
|
||||||
|
|
||||||
|
it = _ctrl_slaves;
|
||||||
|
|
||||||
|
/* find slave with sock */
|
||||||
|
while (it->next && it->sock != sock)
|
||||||
|
it = it->next;
|
||||||
|
|
||||||
|
if (it->sock == sock)
|
||||||
|
{
|
||||||
|
/* shutdown socket */
|
||||||
|
shutdown(sock, SHUT_RDWR);
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
/* remove item from list */
|
||||||
|
if (it == _ctrl_slaves)
|
||||||
|
{
|
||||||
|
if (it->next)
|
||||||
|
_ctrl_slaves = it->next;
|
||||||
|
else
|
||||||
|
_ctrl_slaves = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->prev)
|
||||||
|
{
|
||||||
|
(it->prev)->next = it->next;
|
||||||
|
if (it->next)
|
||||||
|
(it->next)->prev = it->prev;
|
||||||
|
}
|
||||||
|
else if (it->next)
|
||||||
|
(it->next)->prev = NULL;
|
||||||
|
|
||||||
|
xfree(it);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ctrl_command_result(_ctrl_slave_t * slave, int result)
|
||||||
|
{
|
||||||
|
char buf[64] = { 0 };
|
||||||
|
|
||||||
|
/* translate and send result code back to client */
|
||||||
|
if (result == 0)
|
||||||
|
send(slave->sock, "OK\n", 3, 0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(buf, 64, "ERROR %x\n", result);
|
||||||
|
send(slave->sock, buf, strlen(buf), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ctrl_dispatch_command(_ctrl_slave_t * slave)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *cmd;
|
||||||
|
unsigned int res;
|
||||||
|
|
||||||
|
/* unescape linebuffer */
|
||||||
|
cmd = utils_string_unescape(slave->linebuf);
|
||||||
|
if (strncmp(cmd, CMD_SEAMLESS_SPAWN " ", strlen(CMD_SEAMLESS_SPAWN) + 1) == 0)
|
||||||
|
{
|
||||||
|
/* process seamless spawn request */
|
||||||
|
p = strstr(cmd, "seamlessrdpshell.exe");
|
||||||
|
if (p)
|
||||||
|
p += strlen("seamlessrdpshell.exe") + 1;
|
||||||
|
else
|
||||||
|
p = cmd + strlen(CMD_SEAMLESS_SPAWN) + 1;
|
||||||
|
|
||||||
|
res = ERR_RESULT_OK;
|
||||||
|
|
||||||
|
if (seamless_send_spawn(p) == (unsigned int) -1)
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = ERR_RESULT_NO_SUCH_COMMAND;
|
||||||
|
}
|
||||||
|
xfree(cmd);
|
||||||
|
|
||||||
|
_ctrl_command_result(slave, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RD_BOOL
|
||||||
|
_ctrl_verify_unix_socket()
|
||||||
|
{
|
||||||
|
int s, len;
|
||||||
|
struct sockaddr_un saun;
|
||||||
|
|
||||||
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
perror("Error creating ctrl client socket: socket()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
saun.sun_family = AF_UNIX;
|
||||||
|
strcpy(saun.sun_path, ctrlsock_name);
|
||||||
|
len = sizeof(saun.sun_family) + strlen(saun.sun_path);
|
||||||
|
|
||||||
|
/* test connection */
|
||||||
|
if (connect(s, (struct sockaddr *) &saun, len) != 0)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
shutdown(s, SHUT_RDWR);
|
||||||
|
close(s);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ctrl_create_hash(const char *user, const char *domain, const char *host, char *hash, size_t hsize)
|
||||||
|
{
|
||||||
|
RDSSL_SHA1 sha1;
|
||||||
|
uint8 out[20], delim;
|
||||||
|
uint16 version;
|
||||||
|
uint32 flags;
|
||||||
|
|
||||||
|
/* version\0user\0domain\0host\0flags */
|
||||||
|
flags = 0;
|
||||||
|
delim = '\0';
|
||||||
|
version = 0x0100;
|
||||||
|
|
||||||
|
if (g_seamless_rdp)
|
||||||
|
flags = CTRL_HASH_FLAG_SEAMLESS;
|
||||||
|
|
||||||
|
rdssl_sha1_init(&sha1);
|
||||||
|
rdssl_sha1_update(&sha1, (uint8 *) & version, sizeof(version));
|
||||||
|
rdssl_sha1_update(&sha1, &delim, 1);
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
rdssl_sha1_update(&sha1, (uint8 *) user, strlen(user));
|
||||||
|
rdssl_sha1_update(&sha1, &delim, 1);
|
||||||
|
|
||||||
|
if (domain)
|
||||||
|
rdssl_sha1_update(&sha1, (uint8 *) domain, strlen(domain));
|
||||||
|
rdssl_sha1_update(&sha1, &delim, 1);
|
||||||
|
|
||||||
|
if (host)
|
||||||
|
rdssl_sha1_update(&sha1, (uint8 *) host, strlen(host));
|
||||||
|
rdssl_sha1_update(&sha1, &delim, 1);
|
||||||
|
|
||||||
|
rdssl_sha1_update(&sha1, (uint8 *) & flags, sizeof(flags));
|
||||||
|
rdssl_sha1_final(&sha1, out);
|
||||||
|
|
||||||
|
sec_hash_to_string(hash, hsize, out, sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Initialize ctrl
|
||||||
|
Ret values: <0 failure, 0 master, 1 client
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ctrl_init(const char *user, const char *domain, const char *host)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
struct sockaddr_un saun;
|
||||||
|
char hash[41], path[PATH_MAX];
|
||||||
|
char *home;
|
||||||
|
|
||||||
|
/* check if ctrl already initialized */
|
||||||
|
if (ctrlsock != 0 || _ctrl_is_slave)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
home = getenv("HOME");
|
||||||
|
if (home == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get uniq hash for ctrlsock name */
|
||||||
|
_ctrl_create_hash(user, domain, host, hash, 41);
|
||||||
|
snprintf(ctrlsock_name, PATH_MAX, "%s" RDESKTOP_CTRLSOCK_STORE "/%s.ctl", home, hash);
|
||||||
|
ctrlsock_name[sizeof(ctrlsock_name) - 1] = '\0';
|
||||||
|
|
||||||
|
/* make sure that ctrlsock store path exists */
|
||||||
|
snprintf(path, PATH_MAX, "%s" RDESKTOP_CTRLSOCK_STORE, home);
|
||||||
|
path[sizeof(path) - 1] = '\0';
|
||||||
|
if (utils_mkdir_p(path, 0700) == -1)
|
||||||
|
{
|
||||||
|
perror(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if ctrl socket already exist then this process becomes a client */
|
||||||
|
if (stat(ctrlsock_name, &st) == 0)
|
||||||
|
{
|
||||||
|
/* verify that unix socket is not stale */
|
||||||
|
if (_ctrl_verify_unix_socket() == True)
|
||||||
|
{
|
||||||
|
_ctrl_is_slave = True;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unlink(ctrlsock_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup ctrl socket and start listening for connections */
|
||||||
|
if ((ctrlsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
perror("Error creating ctrl socket:");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bind and start listening on server socket */
|
||||||
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
||||||
|
saun.sun_family = AF_UNIX;
|
||||||
|
strncpy(saun.sun_path, ctrlsock_name, sizeof(saun.sun_path));
|
||||||
|
if (bind(ctrlsock, (struct sockaddr *) &saun, sizeof(struct sockaddr_un)) < 0)
|
||||||
|
{
|
||||||
|
perror("Error binding ctrl socket:");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(ctrlsock, 5) < 0)
|
||||||
|
{
|
||||||
|
perror("Error listening on socket:");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add ctrl cleanup func to exit hooks */
|
||||||
|
atexit(ctrl_cleanup);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ctrl_cleanup()
|
||||||
|
{
|
||||||
|
if (ctrlsock)
|
||||||
|
{
|
||||||
|
close(ctrlsock);
|
||||||
|
unlink(ctrlsock_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RD_BOOL
|
||||||
|
ctrl_is_slave()
|
||||||
|
{
|
||||||
|
return _ctrl_is_slave;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ctrl_add_fds(int *n, fd_set * rfds)
|
||||||
|
{
|
||||||
|
_ctrl_slave_t *it;
|
||||||
|
if (ctrlsock == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FD_SET(ctrlsock, rfds);
|
||||||
|
*n = MAX(*n, ctrlsock);
|
||||||
|
|
||||||
|
|
||||||
|
/* add connected slaves to fd set */
|
||||||
|
it = _ctrl_slaves;
|
||||||
|
while (it)
|
||||||
|
{
|
||||||
|
FD_SET(it->sock, rfds);
|
||||||
|
*n = MAX(*n, it->sock);
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ctrl_check_fds(fd_set * rfds, fd_set * wfds)
|
||||||
|
{
|
||||||
|
int ns, res, offs;
|
||||||
|
struct sockaddr_un fsaun;
|
||||||
|
socklen_t fromlen;
|
||||||
|
_ctrl_slave_t *it;
|
||||||
|
|
||||||
|
if (ctrlsock == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(&fsaun, 0, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
|
/* check if we got any connections on server socket */
|
||||||
|
if (FD_ISSET(ctrlsock, rfds))
|
||||||
|
{
|
||||||
|
FD_CLR(ctrlsock, rfds);
|
||||||
|
fromlen = sizeof(fsaun);
|
||||||
|
ns = accept(ctrlsock, (struct sockaddr *) &fsaun, &fromlen);
|
||||||
|
if (ns < 0)
|
||||||
|
{
|
||||||
|
perror("server: accept()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ctrl_slave_new(ns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if any of our slaves fds has data */
|
||||||
|
it = _ctrl_slaves;
|
||||||
|
while (it)
|
||||||
|
{
|
||||||
|
if (FD_ISSET(it->sock, rfds))
|
||||||
|
{
|
||||||
|
offs = strlen(it->linebuf);
|
||||||
|
res = recv(it->sock, it->linebuf + offs, CTRL_LINEBUF_SIZE - offs, 0);
|
||||||
|
FD_CLR(it->sock, rfds);
|
||||||
|
|
||||||
|
/* linebuffer full let's disconnect slave */
|
||||||
|
if (it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\0' &&
|
||||||
|
it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\n')
|
||||||
|
{
|
||||||
|
_ctrl_slave_disconnect(it->sock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res >= 0)
|
||||||
|
{
|
||||||
|
/* Check if we got full command line */
|
||||||
|
char *p;
|
||||||
|
if ((p = strchr(it->linebuf, '\n')) == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* iterate over string and check against escaped \n */
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
/* Check if newline is escaped */
|
||||||
|
if (p > it->linebuf && *(p - 1) != '\\')
|
||||||
|
break;
|
||||||
|
p = strchr(p + 1, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we havent found an nonescaped \n we need more data */
|
||||||
|
if (p == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* strip new linebuf and dispatch command */
|
||||||
|
*p = '\0';
|
||||||
|
_ctrl_dispatch_command(it);
|
||||||
|
memset(it->linebuf, 0, CTRL_LINEBUF_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Peer disconnected or socket error */
|
||||||
|
_ctrl_slave_disconnect(it->sock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_ICONV
|
||||||
|
extern char g_codepage[16];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
ctrl_send_command(const char *cmd, const char *arg)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
struct sockaddr_un saun;
|
||||||
|
int s, len, index, ret;
|
||||||
|
char data[CTRL_LINEBUF_SIZE], tmp[CTRL_LINEBUF_SIZE];
|
||||||
|
char result[CTRL_RESULT_SIZE], c, *escaped;
|
||||||
|
|
||||||
|
escaped = NULL;
|
||||||
|
|
||||||
|
if (!_ctrl_is_slave)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
perror("Error creating ctrl client socket: socket()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
||||||
|
saun.sun_family = AF_UNIX;
|
||||||
|
strcpy(saun.sun_path, ctrlsock_name);
|
||||||
|
len = sizeof(saun.sun_family) + strlen(saun.sun_path);
|
||||||
|
|
||||||
|
if (connect(s, (struct sockaddr *) &saun, len) < 0)
|
||||||
|
{
|
||||||
|
perror("Error connecting to ctrl socket: connect()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bundle cmd and argument into string, convert to UTF-8 if needed */
|
||||||
|
snprintf(data, CTRL_LINEBUF_SIZE, "%s %s", cmd, arg);
|
||||||
|
ret = utils_locale_to_utf8(data, strlen(data), tmp, CTRL_LINEBUF_SIZE - 1);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
goto bail_out;
|
||||||
|
|
||||||
|
/* escape the utf-8 string */
|
||||||
|
escaped = utils_string_escape(tmp);
|
||||||
|
if ((strlen(escaped) + 1) > CTRL_LINEBUF_SIZE - 1)
|
||||||
|
goto bail_out;
|
||||||
|
|
||||||
|
/* send escaped utf-8 command to master */
|
||||||
|
send(s, escaped, strlen(escaped), 0);
|
||||||
|
send(s, "\n", 1, 0);
|
||||||
|
|
||||||
|
/* read result from master */
|
||||||
|
fp = fdopen(s, "r");
|
||||||
|
index = 0;
|
||||||
|
while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != '\n')
|
||||||
|
{
|
||||||
|
result[index] = c;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
result[index - 1] = '\0';
|
||||||
|
|
||||||
|
if (strncmp(result, "ERROR ", 6) == 0)
|
||||||
|
{
|
||||||
|
if (sscanf(result, "ERROR %d", &ret) != 1)
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bail_out:
|
||||||
|
xfree(escaped);
|
||||||
|
shutdown(s, SHUT_RDWR);
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
61
doc/ctrl.txt
Normal file
61
doc/ctrl.txt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
TODO
|
||||||
|
----
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
The protocol is a UTF-8 line based bidirectional protocol were a slave sends
|
||||||
|
a method with optional argument to the master and a result of operation is
|
||||||
|
returned to the slave in a synchronous send recv operation.
|
||||||
|
|
||||||
|
|
||||||
|
A method call from Slave to the Master have the following syntax:
|
||||||
|
|
||||||
|
METHOD [ARG1[ ARG2 [...]]]
|
||||||
|
|
||||||
|
Argument string sent for a method call should be escaped as descibed below under
|
||||||
|
String escaping section.
|
||||||
|
|
||||||
|
|
||||||
|
A result from Master to the Slave have the following syntax:
|
||||||
|
|
||||||
|
"OK" || "ERROR" <CODE>
|
||||||
|
|
||||||
|
Result CODE is specific to the method implementation except for 0xffffffff which
|
||||||
|
is internal to the protocol and means that method does not exist.
|
||||||
|
|
||||||
|
|
||||||
|
One line may not exceed 1024 bytes, including newline.
|
||||||
|
|
||||||
|
|
||||||
|
String escaping
|
||||||
|
===============
|
||||||
|
Percentage sign has been choosen as escaping character.
|
||||||
|
|
||||||
|
The rules for escaping are:
|
||||||
|
|
||||||
|
Characters < 32 and % are escaped to %xx where xx is the byte value of the
|
||||||
|
escaped character.
|
||||||
|
|
||||||
|
|
||||||
|
Implemented methods
|
||||||
|
=======================
|
||||||
|
|
||||||
|
seamless.spawn
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Spawns a new windows command in the current seamless channel of the master.
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
seamless.spawn <COMMANDLINE>
|
||||||
|
|
||||||
|
Spawns a seamless process in the current seamless channel of the master with COMMANDLINE.
|
||||||
|
|
||||||
|
Errorcodes:
|
||||||
|
|
||||||
|
0x1 Master does not have a seamless rdp channel established.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,6 +86,10 @@ toggled at any time using Ctrl-Alt-Enter.
|
|||||||
Force the server to send screen updates as bitmaps rather than using
|
Force the server to send screen updates as bitmaps rather than using
|
||||||
higher-level drawing operations.
|
higher-level drawing operations.
|
||||||
.TP
|
.TP
|
||||||
|
.BR "-t"
|
||||||
|
Disable use of remote control. This will disable features like seamless connection
|
||||||
|
sharing.
|
||||||
|
.TP
|
||||||
.BR "-A"
|
.BR "-A"
|
||||||
Enable SeamlessRDP. In this mode, rdesktop creates a X11 window for
|
Enable SeamlessRDP. In this mode, rdesktop creates a X11 window for
|
||||||
each window on the server side. This mode requires the SeamlessRDP
|
each window on the server side. This mode requires the SeamlessRDP
|
||||||
|
16
proto.h
16
proto.h
@ -24,6 +24,12 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
/* *INDENT-ON* */
|
/* *INDENT-ON* */
|
||||||
|
/* utils.c */
|
||||||
|
char *utils_string_escape(const char *str);
|
||||||
|
char *utils_string_unescape(const char *str);
|
||||||
|
int utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os);
|
||||||
|
int utils_mkdir_safe(const char *path, int mask);
|
||||||
|
int utils_mkdir_p(const char *path, int mask);
|
||||||
/* bitmap.c */
|
/* bitmap.c */
|
||||||
RD_BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp);
|
RD_BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp);
|
||||||
/* cache.c */
|
/* cache.c */
|
||||||
@ -57,6 +63,14 @@ void cliprdr_send_data_request(uint32 format);
|
|||||||
void cliprdr_send_data(uint8 * data, uint32 length);
|
void cliprdr_send_data(uint8 * data, uint32 length);
|
||||||
void cliprdr_set_mode(const char *optarg);
|
void cliprdr_set_mode(const char *optarg);
|
||||||
RD_BOOL cliprdr_init(void);
|
RD_BOOL cliprdr_init(void);
|
||||||
|
/* ctrl.c */
|
||||||
|
int ctrl_init(const char *user, const char *domain, const char *host);
|
||||||
|
void ctrl_cleanup();
|
||||||
|
RD_BOOL ctrl_is_slave();
|
||||||
|
int ctrl_send_command(const char *cmd, const char *args);
|
||||||
|
void ctrl_add_fds(int *n, fd_set * rfds);
|
||||||
|
void ctrl_check_fds(fd_set * rfds, fd_set * wfds);
|
||||||
|
|
||||||
/* disk.c */
|
/* disk.c */
|
||||||
int disk_enum_devices(uint32 * id, char *optarg);
|
int disk_enum_devices(uint32 * id, char *optarg);
|
||||||
RD_NTSTATUS disk_query_information(RD_NTHANDLE handle, uint32 info_class, STREAM out);
|
RD_NTSTATUS disk_query_information(RD_NTHANDLE handle, uint32 info_class, STREAM out);
|
||||||
@ -327,6 +341,8 @@ void seamless_select_timeout(struct timeval *tv);
|
|||||||
unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags);
|
unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags);
|
||||||
unsigned int seamless_send_focus(unsigned long id, unsigned long flags);
|
unsigned int seamless_send_focus(unsigned long id, unsigned long flags);
|
||||||
unsigned int seamless_send_destroy(unsigned long id);
|
unsigned int seamless_send_destroy(unsigned long id);
|
||||||
|
unsigned int seamless_send_spawn(char *cmd);
|
||||||
|
|
||||||
/* scard.c */
|
/* scard.c */
|
||||||
void scard_lock(int lock);
|
void scard_lock(int lock);
|
||||||
void scard_unlock(int lock);
|
void scard_unlock(int lock);
|
||||||
|
89
rdesktop.c
89
rdesktop.c
@ -3,7 +3,7 @@
|
|||||||
Entrypoint and utility functions
|
Entrypoint and utility functions
|
||||||
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
||||||
Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
|
Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||||
Copyright 2010-2011 Henrik Andersson <hean01@cendio.se> for Cendio AB
|
Copyright 2010-2013 Henrik Andersson <hean01@cendio.se> for Cendio AB
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -85,6 +85,7 @@ RD_BOOL g_sendmotion = True;
|
|||||||
RD_BOOL g_bitmap_cache = True;
|
RD_BOOL g_bitmap_cache = True;
|
||||||
RD_BOOL g_bitmap_cache_persist_enable = False;
|
RD_BOOL g_bitmap_cache_persist_enable = False;
|
||||||
RD_BOOL g_bitmap_cache_precache = True;
|
RD_BOOL g_bitmap_cache_precache = True;
|
||||||
|
RD_BOOL g_use_ctrl = True;
|
||||||
RD_BOOL g_encryption = True;
|
RD_BOOL g_encryption = True;
|
||||||
RD_BOOL g_packet_encryption = True;
|
RD_BOOL g_packet_encryption = True;
|
||||||
RD_BOOL g_desktop_save = True; /* desktop save order */
|
RD_BOOL g_desktop_save = True; /* desktop save order */
|
||||||
@ -178,6 +179,7 @@ usage(char *program)
|
|||||||
fprintf(stderr, " -K: keep window manager key bindings\n");
|
fprintf(stderr, " -K: keep window manager key bindings\n");
|
||||||
fprintf(stderr, " -S: caption button size (single application mode)\n");
|
fprintf(stderr, " -S: caption button size (single application mode)\n");
|
||||||
fprintf(stderr, " -T: window title\n");
|
fprintf(stderr, " -T: window title\n");
|
||||||
|
fprintf(stderr, " -t: disable use of remote ctrl\n");
|
||||||
fprintf(stderr, " -N: enable numlock syncronization\n");
|
fprintf(stderr, " -N: enable numlock syncronization\n");
|
||||||
fprintf(stderr, " -X: embed into another window with a given id.\n");
|
fprintf(stderr, " -X: embed into another window with a given id.\n");
|
||||||
fprintf(stderr, " -a: connection colour depth\n");
|
fprintf(stderr, " -a: connection colour depth\n");
|
||||||
@ -580,6 +582,9 @@ main(int argc, char *argv[])
|
|||||||
flags |= RDP_LOGON_PASSWORD_IS_SC_PIN;
|
flags |= RDP_LOGON_PASSWORD_IS_SC_PIN;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case 't':
|
||||||
|
g_use_ctrl = False;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
STRNCPY(g_hostname, optarg, sizeof(g_hostname));
|
STRNCPY(g_hostname, optarg, sizeof(g_hostname));
|
||||||
@ -979,6 +984,24 @@ main(int argc, char *argv[])
|
|||||||
return EX_OK;
|
return EX_OK;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
/* Only startup ctrl functionality is seamless are used for now. */
|
||||||
|
if (g_use_ctrl && g_seamless_rdp)
|
||||||
|
{
|
||||||
|
if (ctrl_init(server, domain, g_username) < 0)
|
||||||
|
{
|
||||||
|
error("Failed to initialize ctrl mode.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctrl_is_slave())
|
||||||
|
{
|
||||||
|
fprintf(stdout,
|
||||||
|
"rdesktop in slave mode sending command to master process.\n");
|
||||||
|
|
||||||
|
return ctrl_send_command("seamless.spawn", shell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ui_init())
|
if (!ui_init())
|
||||||
return EX_OSERR;
|
return EX_OSERR;
|
||||||
|
|
||||||
@ -1510,68 +1533,6 @@ l_to_a(long N, int base)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
safe_mkdir(const char *path, int mask)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
res = stat(path, &st);
|
|
||||||
if (res == -1)
|
|
||||||
return mkdir(path, mask);
|
|
||||||
|
|
||||||
if (!S_ISDIR(st.st_mode))
|
|
||||||
{
|
|
||||||
errno = EEXIST;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
mkdir_p(const char *path, int mask)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
char *ptok;
|
|
||||||
char pt[PATH_MAX];
|
|
||||||
char bp[PATH_MAX];
|
|
||||||
|
|
||||||
if (!path || strlen(path) == 0)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (strlen(path) > PATH_MAX)
|
|
||||||
{
|
|
||||||
errno = E2BIG;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
pt[0] = bp[0] = '\0';
|
|
||||||
strcpy(bp, path);
|
|
||||||
|
|
||||||
ptok = strtok(bp, "/");
|
|
||||||
if (ptok == NULL)
|
|
||||||
return safe_mkdir(path, mask);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (ptok != bp)
|
|
||||||
strcat(pt, "/");
|
|
||||||
|
|
||||||
strcat(pt, ptok);
|
|
||||||
res = safe_mkdir(pt, mask);
|
|
||||||
if (res != 0)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
}
|
|
||||||
while ((ptok = strtok(NULL, "/")) != NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
load_licence(unsigned char **data)
|
load_licence(unsigned char **data)
|
||||||
{
|
{
|
||||||
@ -1626,7 +1587,7 @@ save_licence(unsigned char *data, int length)
|
|||||||
|
|
||||||
snprintf(path, PATH_MAX, "%s" RDESKTOP_LICENSE_STORE, home);
|
snprintf(path, PATH_MAX, "%s" RDESKTOP_LICENSE_STORE, home);
|
||||||
path[sizeof(path) - 1] = '\0';
|
path[sizeof(path) - 1] = '\0';
|
||||||
if (mkdir_p(path, 0700) == -1)
|
if (utils_mkdir_p(path, 0700) == -1)
|
||||||
{
|
{
|
||||||
perror(path);
|
perror(path);
|
||||||
return;
|
return;
|
||||||
|
20
seamless.c
20
seamless.c
@ -3,6 +3,7 @@
|
|||||||
Seamless Windows support
|
Seamless Windows support
|
||||||
Copyright 2005-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
|
Copyright 2005-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||||
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||||
|
Copyright 2013 Henrik Andersson <hean01@cendio.se> for Cendio AB
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -411,7 +412,7 @@ seamless_send(const char *command, const char *format, ...)
|
|||||||
STREAM s;
|
STREAM s;
|
||||||
size_t len;
|
size_t len;
|
||||||
va_list argp;
|
va_list argp;
|
||||||
char buf[1025];
|
char *escaped, buf[1025];
|
||||||
|
|
||||||
len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, seamless_serial);
|
len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, seamless_serial);
|
||||||
|
|
||||||
@ -423,6 +424,11 @@ seamless_send(const char *command, const char *format, ...)
|
|||||||
|
|
||||||
assert(len < (sizeof(buf) - 1));
|
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] = '\n';
|
||||||
buf[len + 1] = '\0';
|
buf[len + 1] = '\0';
|
||||||
|
|
||||||
@ -515,3 +521,15 @@ seamless_send_destroy(unsigned long id)
|
|||||||
{
|
{
|
||||||
return seamless_send("DESTROY", "0x%08lx", 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;
|
||||||
|
}
|
||||||
|
224
utils.c
Normal file
224
utils.c
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/* -*- c-basic-offset: 8 -*-
|
||||||
|
rdesktop: A Remote Desktop Protocol client.
|
||||||
|
Generic utility functions
|
||||||
|
Copyright 2013 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 <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef HAVE_ICONV_H
|
||||||
|
#include <iconv.h>
|
||||||
|
#endif
|
||||||
|
#include "rdesktop.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_ICONV
|
||||||
|
extern char g_codepage[16];
|
||||||
|
static RD_BOOL g_iconv_works = True;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
utils_string_escape(const char *str)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
char *pe, *e, esc[4];
|
||||||
|
size_t es;
|
||||||
|
int cnt;
|
||||||
|
|
||||||
|
/* count indices */
|
||||||
|
cnt = 0;
|
||||||
|
p = str;
|
||||||
|
while (*(p++) != '\0')
|
||||||
|
if ((unsigned char) *p < 32 || *p == '%')
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
/* if no characters needs escaping return copy of str */
|
||||||
|
if (cnt == 0)
|
||||||
|
return strdup(str);
|
||||||
|
|
||||||
|
/* allocate new mem for result */
|
||||||
|
es = strlen(str) + (cnt * 3) + 1;
|
||||||
|
pe = e = xmalloc(es);
|
||||||
|
memset(e, 0, es);
|
||||||
|
p = str;
|
||||||
|
while (*p != '\0')
|
||||||
|
{
|
||||||
|
if ((unsigned char) *p < 32 || *p == '%')
|
||||||
|
{
|
||||||
|
snprintf(esc, 4, "%%%02X", *p);
|
||||||
|
memcpy(pe, esc, 3);
|
||||||
|
pe += 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pe = *p;
|
||||||
|
pe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
utils_string_unescape(const char *str)
|
||||||
|
{
|
||||||
|
char *ns, *ps, *pd, c;
|
||||||
|
|
||||||
|
ns = xmalloc(strlen(str) + 1);
|
||||||
|
memcpy(ns, str, strlen(str) + 1);
|
||||||
|
ps = pd = ns;
|
||||||
|
|
||||||
|
while (*ps != '\0')
|
||||||
|
{
|
||||||
|
/* check if found escaped character */
|
||||||
|
if (ps[0] == '%')
|
||||||
|
{
|
||||||
|
if (sscanf(ps, "%%%2hhX", &c) == 1)
|
||||||
|
{
|
||||||
|
pd[0] = c;
|
||||||
|
ps += 3;
|
||||||
|
pd++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* just copy over the char */
|
||||||
|
*pd = *ps;
|
||||||
|
ps++;
|
||||||
|
pd++;
|
||||||
|
}
|
||||||
|
pd[0] = '\0';
|
||||||
|
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
utils_mkdir_safe(const char *path, int mask)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
res = stat(path, &st);
|
||||||
|
if (res == -1)
|
||||||
|
return mkdir(path, mask);
|
||||||
|
|
||||||
|
if (!S_ISDIR(st.st_mode))
|
||||||
|
{
|
||||||
|
errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
utils_mkdir_p(const char *path, int mask)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
char *ptok;
|
||||||
|
char pt[PATH_MAX];
|
||||||
|
char bp[PATH_MAX];
|
||||||
|
|
||||||
|
if (!path || strlen(path) == 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strlen(path) > PATH_MAX)
|
||||||
|
{
|
||||||
|
errno = E2BIG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
pt[0] = bp[0] = '\0';
|
||||||
|
strcpy(bp, path);
|
||||||
|
|
||||||
|
ptok = strtok(bp, "/");
|
||||||
|
if (ptok == NULL)
|
||||||
|
return utils_mkdir_safe(path, mask);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (ptok != bp)
|
||||||
|
strcat(pt, "/");
|
||||||
|
|
||||||
|
strcat(pt, ptok);
|
||||||
|
res = utils_mkdir_safe(pt, mask);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
while ((ptok = strtok(NULL, "/")) != NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert from system locale string to utf-8 */
|
||||||
|
int
|
||||||
|
utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_ICONV
|
||||||
|
static iconv_t *iconv_h = (iconv_t) - 1;
|
||||||
|
if (strncmp(g_codepage, "UTF-8", strlen("UTF-8")) == 0)
|
||||||
|
goto pass_trough_as_is;
|
||||||
|
|
||||||
|
if (g_iconv_works == False)
|
||||||
|
goto pass_trough_as_is;
|
||||||
|
|
||||||
|
/* if not already initialize */
|
||||||
|
if (iconv_h == (iconv_t) - 1)
|
||||||
|
{
|
||||||
|
if ((iconv_h = iconv_open("UTF-8", g_codepage)) == (iconv_t) - 1)
|
||||||
|
{
|
||||||
|
warning("utils_string_to_utf8: iconv_open[%s -> %s] fail %p\n",
|
||||||
|
g_codepage, "UTF-8", iconv_h);
|
||||||
|
|
||||||
|
g_iconv_works = False;
|
||||||
|
goto pass_trough_as_is;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert string */
|
||||||
|
if (iconv(iconv_h, (ICONV_CONST char **) &src, &is, &dest, &os) == (size_t) - 1)
|
||||||
|
{
|
||||||
|
iconv_close(iconv_h);
|
||||||
|
iconv_h = (iconv_t) - 1;
|
||||||
|
warning("utils_string_to_utf8: iconv(1) fail, errno %d\n", errno);
|
||||||
|
|
||||||
|
g_iconv_works = False;
|
||||||
|
goto pass_trough_as_is;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Out couldn't hold the entire convertion */
|
||||||
|
if (is != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
pass_trough_as_is:
|
||||||
|
/* can dest hold strcpy of src */
|
||||||
|
if (os < (strlen(src) + 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(dest, src, strlen(src) + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
6
xwin.c
6
xwin.c
@ -4,6 +4,7 @@
|
|||||||
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
||||||
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||||
Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
|
Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||||
|
Copyright 2012-2013 Henrik Andersson <hean01@cendio.se> for Cendio AB
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -2668,6 +2669,9 @@ ui_select(int rdp_socket)
|
|||||||
rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
|
rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
|
||||||
seamless_select_timeout(&tv);
|
seamless_select_timeout(&tv);
|
||||||
|
|
||||||
|
/* add ctrl slaves handles */
|
||||||
|
ctrl_add_fds(&n, &rfds);
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
switch (select(n, &rfds, &wfds, NULL, &tv))
|
switch (select(n, &rfds, &wfds, NULL, &tv))
|
||||||
@ -2692,6 +2696,8 @@ ui_select(int rdp_socket)
|
|||||||
|
|
||||||
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
|
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
|
||||||
|
|
||||||
|
ctrl_check_fds(&rfds, &wfds);
|
||||||
|
|
||||||
if (FD_ISSET(rdp_socket, &rfds))
|
if (FD_ISSET(rdp_socket, &rfds))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user