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:
Henrik Andersson 2013-01-21 12:28:14 +00:00
parent 15e4518b13
commit be93803129
9 changed files with 875 additions and 67 deletions

View File

@ -28,8 +28,8 @@ SOUNDOBJ = @SOUNDOBJ@
SCARDOBJ = @SCARDOBJ@
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
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.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 ctrl.o
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
.PHONY: all

518
ctrl.c Normal file
View 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
View 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.

View File

@ -86,6 +86,10 @@ toggled at any time using Ctrl-Alt-Enter.
Force the server to send screen updates as bitmaps rather than using
higher-level drawing operations.
.TP
.BR "-t"
Disable use of remote control. This will disable features like seamless connection
sharing.
.TP
.BR "-A"
Enable SeamlessRDP. In this mode, rdesktop creates a X11 window for
each window on the server side. This mode requires the SeamlessRDP

16
proto.h
View File

@ -24,6 +24,12 @@
extern "C" {
#endif
/* *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 */
RD_BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp);
/* cache.c */
@ -57,6 +63,14 @@ void cliprdr_send_data_request(uint32 format);
void cliprdr_send_data(uint8 * data, uint32 length);
void cliprdr_set_mode(const char *optarg);
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 */
int disk_enum_devices(uint32 * id, char *optarg);
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_focus(unsigned long id, unsigned long flags);
unsigned int seamless_send_destroy(unsigned long id);
unsigned int seamless_send_spawn(char *cmd);
/* scard.c */
void scard_lock(int lock);
void scard_unlock(int lock);

View File

@ -3,7 +3,7 @@
Entrypoint and utility functions
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
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
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_persist_enable = False;
RD_BOOL g_bitmap_cache_precache = True;
RD_BOOL g_use_ctrl = True;
RD_BOOL g_encryption = True;
RD_BOOL g_packet_encryption = True;
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, " -S: caption button size (single application mode)\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, " -X: embed into another window with a given id.\n");
fprintf(stderr, " -a: connection colour depth\n");
@ -580,6 +582,9 @@ main(int argc, char *argv[])
flags |= RDP_LOGON_PASSWORD_IS_SC_PIN;
break;
#endif
case 't':
g_use_ctrl = False;
break;
case 'n':
STRNCPY(g_hostname, optarg, sizeof(g_hostname));
@ -979,6 +984,24 @@ main(int argc, char *argv[])
return EX_OK;
#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())
return EX_OSERR;
@ -1510,68 +1533,6 @@ l_to_a(long N, int base)
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
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);
path[sizeof(path) - 1] = '\0';
if (mkdir_p(path, 0700) == -1)
if (utils_mkdir_p(path, 0700) == -1)
{
perror(path);
return;

View File

@ -3,6 +3,7 @@
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 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
@ -411,7 +412,7 @@ seamless_send(const char *command, const char *format, ...)
STREAM s;
size_t len;
va_list argp;
char buf[1025];
char *escaped, buf[1025];
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));
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';
@ -515,3 +521,15 @@ 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;
}

224
utils.c Normal file
View 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
View File

@ -4,6 +4,7 @@
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
Copyright 2007-2008 Pierre Ossman <ossman@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
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);
seamless_select_timeout(&tv);
/* add ctrl slaves handles */
ctrl_add_fds(&n, &rfds);
n++;
switch (select(n, &rfds, &wfds, NULL, &tv))
@ -2692,6 +2696,8 @@ ui_select(int rdp_socket)
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
ctrl_check_fds(&rfds, &wfds);
if (FD_ISSET(rdp_socket, &rfds))
return 1;