Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- connections.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "inet.h"
     37 #include "remote.h"
     38 #include "keyboard.h"
     39 #include "cleanup.h"
     40 #include "gui.h"
     41 #include "solid.h"
     42 #include "rates.h"
     43 #include "screen.h"
     44 #include "unixpw.h"
     45 #include "user.h"
     46 #include "scan.h"
     47 #include "sslcmds.h"
     48 #include "sslhelper.h"
     49 #include "xwrappers.h"
     50 #include "xevents.h"
     51 #include "win_utils.h"
     52 #include "macosx.h"
     53 #include "macosxCG.h"
     54 #include "userinput.h"
     55 #include "pointer.h"
     56 #include "xrandr.h"
     57 
     58 /*
     59  * routines for handling incoming, outgoing, etc connections
     60  */
     61 
     62 /* string for the VNC_CONNECT property */
     63 char vnc_connect_str[VNC_CONNECT_MAX+1];
     64 Atom vnc_connect_prop = None;
     65 char x11vnc_remote_str[X11VNC_REMOTE_MAX+1];
     66 Atom x11vnc_remote_prop = None;
     67 rfbClientPtr inetd_client = NULL;
     68 
     69 int all_clients_initialized(void);
     70 char *list_clients(void);
     71 int new_fb_size_clients(rfbScreenInfoPtr s);
     72 void close_all_clients(void);
     73 void close_clients(char *str);
     74 void set_client_input(char *str);
     75 void set_child_info(void);
     76 int cmd_ok(char *cmd);
     77 void client_gone(rfbClientPtr client);
     78 void client_gone_chat_helper(rfbClientPtr client);
     79 void reverse_connect(char *str);
     80 void set_vnc_connect_prop(char *str);
     81 void read_vnc_connect_prop(int);
     82 void set_x11vnc_remote_prop(char *str);
     83 void read_x11vnc_remote_prop(int);
     84 void check_connect_inputs(void);
     85 void check_gui_inputs(void);
     86 rfbClientPtr create_new_client(int sock, int start_thread);
     87 enum rfbNewClientAction new_client(rfbClientPtr client);
     88 enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
     89 rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
     90 void start_client_info_sock(char *host_port_cookie);
     91 void send_client_info(char *str);
     92 void adjust_grabs(int grab, int quiet);
     93 void check_new_clients(void);
     94 int accept_client(rfbClientPtr client);
     95 void check_ipv6_listen(long usec);
     96 void check_unix_sock(long usec);
     97 int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
     98     int len, FILE *output);
     99 int check_access(char *addr);
    100 void client_set_net(rfbClientPtr client);
    101 char *get_xprop(char *prop, Window win);
    102 int set_xprop(char *prop, Window win, char *value);
    103 char *bcx_xattach(char *str, int *pg_init, int *kg_init);
    104 void grab_state(int *ptr_grabbed, int *kbd_grabbed);
    105 char *wininfo(Window win, int show_children);
    106 
    107 static rfbClientPtr *client_match(char *str);
    108 static void free_client_data(rfbClientPtr client);
    109 static void ugly_geom(char *p, int *x, int *y);
    110 static int ugly_window(char *addr, char *userhost, int X, int Y,
    111     int timeout, char *mode, int accept);
    112 static int action_match(char *action, int rc);
    113 static void check_connect_file(char *file);
    114 static void send_client_connect(void);
    115 
    116 
    117 /*
    118  * check that all clients are in RFB_NORMAL state
    119  */
    120 int all_clients_initialized(void) {
    121 	rfbClientIteratorPtr iter;
    122 	rfbClientPtr cl;
    123 	int ok = 1;
    124 
    125 	if (! screen) {
    126 		return ok;
    127 	}
    128 
    129 	iter = rfbGetClientIterator(screen);
    130 	while( (cl = rfbClientIteratorNext(iter)) ) {
    131 		if (cl->state != RFB_NORMAL) {
    132 			ok = 0;
    133 		} else {
    134 			client_normal_count++;
    135 		}
    136 	}
    137 	rfbReleaseClientIterator(iter);
    138 
    139 	return ok;
    140 }
    141 
    142 char *list_clients(void) {
    143 	rfbClientIteratorPtr iter;
    144 	rfbClientPtr cl;
    145 	char *list, tmp[256];
    146 	int count = 0;
    147 
    148 	if (!screen) {
    149 		return strdup("");
    150 	}
    151 
    152 	iter = rfbGetClientIterator(screen);
    153 	while( (cl = rfbClientIteratorNext(iter)) ) {
    154 		client_set_net(cl);
    155 		count++;
    156 	}
    157 	rfbReleaseClientIterator(iter);
    158 
    159 	/*
    160 	 * each client:
    161          * <id>:<ip>:<port>:<user>:<unix>:<hostname>:<input>:<loginview>:<time>,
    162 	 * 8+1+64+1+5+1+24+1+24+1+256+1+5+1+1+1+10+1
    163 	 * 123.123.123.123:60000/0x11111111-rw,
    164 	 * so count+1 * 1000 must cover it.
    165 	 */
    166 	list = (char *) malloc((count+1)*1000);
    167 
    168 	list[0] = '\0';
    169 
    170 	iter = rfbGetClientIterator(screen);
    171 	while( (cl = rfbClientIteratorNext(iter)) ) {
    172 		ClientData *cd = (ClientData *) cl->clientData;
    173 		char *tmp_host, *p;
    174 
    175 		if (! cd) {
    176 			continue;
    177 		}
    178 		if (*list != '\0') {
    179 			strcat(list, ",");
    180 		}
    181 		sprintf(tmp, "0x%x:", cd->uid);
    182 		strcat(list, tmp);
    183 		p = tmp_host = strdup(cl->host);
    184 		while (*p) {
    185 			if (*p == ':') *p = '#';
    186 			p++;
    187 		}
    188 		strcat(list, tmp_host);
    189 		free(tmp_host);
    190 		strcat(list, ":");
    191 		sprintf(tmp, "%d:", cd->client_port);
    192 		strcat(list, tmp);
    193 		if (cd->username[0] == '\0') {
    194 			char *s = ident_username(cl);
    195 			if (s) free(s);
    196 		}
    197 		if (strstr(cd->username, "UNIX:") == cd->username) {
    198 			strcat(list, cd->username + strlen("UNIX:"));
    199 		} else {
    200 			strcat(list, cd->username);
    201 		}
    202 		strcat(list, ":");
    203 		if (cd->unixname[0] == '\0') {
    204 			strcat(list, "none");
    205 		} else {
    206 			strcat(list, cd->unixname);
    207 		}
    208 		strcat(list, ":");
    209 		p = tmp_host = strdup(cd->hostname);
    210 		while (*p) {
    211 			if (*p == ':') *p = '#';
    212 			p++;
    213 		}
    214 		strcat(list, tmp_host);
    215 		free(tmp_host);
    216 		strcat(list, ":");
    217 		strcat(list, cd->input);
    218 		strcat(list, ":");
    219 		sprintf(tmp, "%d", cd->login_viewonly);
    220 		strcat(list, tmp);
    221 		strcat(list, ":");
    222 		sprintf(tmp, "%d", (int) cd->login_time);
    223 		strcat(list, tmp);
    224 	}
    225 	rfbReleaseClientIterator(iter);
    226 	return list;
    227 }
    228 
    229 /* count number of clients supporting NewFBSize */
    230 int new_fb_size_clients(rfbScreenInfoPtr s) {
    231 	rfbClientIteratorPtr iter;
    232 	rfbClientPtr cl;
    233 	int count = 0;
    234 
    235 	if (! s) {
    236 		return 0;
    237 	}
    238 
    239 	iter = rfbGetClientIterator(s);
    240 	while( (cl = rfbClientIteratorNext(iter)) ) {
    241 		if (cl->useNewFBSize) {
    242 			count++;
    243 		}
    244 	}
    245 	rfbReleaseClientIterator(iter);
    246 	return count;
    247 }
    248 
    249 void close_all_clients(void) {
    250 	rfbClientIteratorPtr iter;
    251 	rfbClientPtr cl;
    252 
    253 	if (! screen) {
    254 		return;
    255 	}
    256 
    257 	iter = rfbGetClientIterator(screen);
    258 	while( (cl = rfbClientIteratorNext(iter)) ) {
    259 		rfbCloseClient(cl);
    260 		rfbClientConnectionGone(cl);
    261 	}
    262 	rfbReleaseClientIterator(iter);
    263 }
    264 
    265 static rfbClientPtr *client_match(char *str) {
    266 	rfbClientIteratorPtr iter;
    267 	rfbClientPtr cl, *cl_list;
    268 	int i, n, host_warn = 0, hex_warn = 0;
    269 
    270 	n = client_count + 10;
    271 	cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr));
    272 
    273 	i = 0;
    274 	iter = rfbGetClientIterator(screen);
    275 	while( (cl = rfbClientIteratorNext(iter)) ) {
    276 		ClientData *cd = (ClientData *) cl->clientData;
    277 		if (strstr(str, "0x") == str) {
    278 			unsigned int in;
    279 			int id;
    280 			if (! cd) {
    281 				continue;
    282 			}
    283 			if (sscanf(str, "0x%x", &in) != 1) {
    284 				if (hex_warn++) {
    285 					continue;
    286 				}
    287 				rfbLog("skipping invalid client hex id: %s\n",
    288 				    str);
    289 				continue;
    290 			}
    291 			id = (unsigned int) in;
    292 			if (cd->uid == id) {
    293 				cl_list[i++] = cl;
    294 			}
    295 		} else {
    296 			int port = -1;
    297 			char *rstr = strdup(str);
    298 			char *q = strrchr(rstr, ':');
    299 			if (q) {
    300 				port = atoi(q+1);
    301 				*q = '\0';
    302 				if (port == 0 && q[1] != '0') {
    303 					port = -1;
    304 				} else if (port < 0) {
    305 					port = -port;
    306 				} else if (port < 200) {
    307 					port = 5500 + port;
    308 				}
    309 			}
    310 			if (ipv6_ip(str)) {
    311 				;
    312 			} else if (! dotted_ip(str, 0)) {
    313 				char *orig = rstr;
    314 				rstr = host2ip(rstr);
    315 				free(orig);
    316 				if (rstr == NULL || *rstr == '\0') {
    317 					if (host_warn++) {
    318 						continue;
    319 					}
    320 					rfbLog("skipping bad lookup: \"%s\"\n", str);
    321 					continue;
    322 				}
    323 				rfbLog("lookup: %s -> %s port=%d\n", str, rstr, port);
    324 			}
    325 			if (!strcmp(rstr, cl->host)) {
    326 				int ok = 1;
    327 				if (port > 0) {
    328 					if (cd != NULL && cd->client_port > 0) {
    329 						if (cd->client_port != port) {
    330 							ok = 0;
    331 						}
    332 					} else {
    333 						int cport = get_remote_port(cl->sock);
    334 						if (cport != port) {
    335 							ok = 0;
    336 						}
    337 					}
    338 				}
    339 				if (ok) {
    340 					cl_list[i++] = cl;
    341 				}
    342 			}
    343 			free(rstr);
    344 		}
    345 		if (i >= n - 1) {
    346 			break;
    347 		}
    348 	}
    349 	rfbReleaseClientIterator(iter);
    350 
    351 	cl_list[i] = NULL;
    352 
    353 	return cl_list;
    354 }
    355 
    356 void close_clients(char *str) {
    357 	rfbClientPtr *cl_list, *cp;
    358 
    359 	if (!strcmp(str, "all") || !strcmp(str, "*")) {
    360 		close_all_clients();
    361 		return;
    362 	}
    363 
    364 	if (! screen) {
    365 		return;
    366 	}
    367 
    368 	cl_list = client_match(str);
    369 
    370 	cp = cl_list;
    371 	while (*cp) {
    372 		rfbCloseClient(*cp);
    373 		rfbClientConnectionGone(*cp);
    374 		cp++;
    375 	}
    376 	free(cl_list);
    377 }
    378 
    379 void set_client_input(char *str) {
    380 	rfbClientPtr *cl_list, *cp;
    381 	char *p, *val;
    382 
    383 	/* str is "match:value" */
    384 
    385 	if (! screen) {
    386 		return;
    387 	}
    388 
    389 	p = strrchr(str, ':');
    390 	if (! p) {
    391 		return;
    392 	}
    393 	*p = '\0';
    394 	p++;
    395 	val = short_kmbcf(p);
    396 
    397 	cl_list = client_match(str);
    398 
    399 	cp = cl_list;
    400 	while (*cp) {
    401 		ClientData *cd = (ClientData *) (*cp)->clientData;
    402 		if (! cd) {
    403 			continue;
    404 		}
    405 		cd->input[0] = '\0';
    406 		strcat(cd->input, "_");
    407 		strcat(cd->input, val);
    408 		cp++;
    409 	}
    410 
    411 	free(val);
    412 	free(cl_list);
    413 }
    414 
    415 void set_child_info(void) {
    416 	char pid[16];
    417 	/* set up useful environment for child process */
    418 	sprintf(pid, "%d", (int) getpid());
    419 	set_env("X11VNC_PID", pid);
    420 	if (program_name) {
    421 		/* e.g. for remote control -R */
    422 		set_env("X11VNC_PROG", program_name);
    423 	}
    424 	if (program_cmdline) {
    425 		set_env("X11VNC_CMDLINE", program_cmdline);
    426 	}
    427 	if (raw_fb_str) {
    428 		set_env("X11VNC_RAWFB_STR", raw_fb_str);
    429 	} else {
    430 		set_env("X11VNC_RAWFB_STR", "");
    431 	}
    432 }
    433 
    434 int cmd_ok(char *cmd) {
    435 	char *p, *str;
    436 	if (no_external_cmds) {
    437 		return 0;
    438 	}
    439 	if (! cmd || cmd[0] == '\0') {
    440 		return 0;
    441 	}
    442 	if (! allowed_external_cmds) {
    443 		/* default, allow any (overridden by -nocmds) */
    444 		return 1;
    445 	}
    446 
    447 	str = strdup(allowed_external_cmds);
    448 	p = strtok(str, ",");
    449 	while (p) {
    450 		if (!strcmp(p, cmd)) {
    451 			free(str);
    452 			return 1;
    453 		}
    454 		p = strtok(NULL, ",");
    455 	}
    456 	free(str);
    457 	return 0;
    458 }
    459 
    460 /*
    461  * utility to run a user supplied command setting some RFB_ env vars.
    462  * used by, e.g., accept_client() and client_gone()
    463  */
    464 int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
    465    int len, FILE *output) {
    466 	char *old_display = NULL;
    467 	char *addr = NULL;
    468 	char str[100];
    469 	int rc, ok;
    470 	ClientData *cd = NULL;
    471 	client_set_net(client);
    472 	if (client != NULL) {
    473 		cd = (ClientData *) client->clientData;
    474 		addr = client->host;
    475 	}
    476 
    477 	if (addr == NULL || addr[0] == '\0') {
    478 		addr = "unknown-host";
    479 	}
    480 
    481 	/* set RFB_CLIENT_ID to semi unique id for command to use */
    482 	if (cd && cd->uid) {
    483 		sprintf(str, "0x%x", cd->uid);
    484 	} else {
    485 		/* not accepted yet: */
    486 		sprintf(str, "0x%x", clients_served);
    487 	}
    488 	set_env("RFB_CLIENT_ID", str);
    489 
    490 	/* set RFB_CLIENT_IP to IP addr for command to use */
    491 	set_env("RFB_CLIENT_IP", addr);
    492 
    493 	/* set RFB_X11VNC_PID to our pid for command to use */
    494 	sprintf(str, "%d", (int) getpid());
    495 	set_env("RFB_X11VNC_PID", str);
    496 
    497 	if (client == NULL) {
    498 		;
    499 	} else if (client->state == RFB_PROTOCOL_VERSION) {
    500 		set_env("RFB_STATE", "PROTOCOL_VERSION");
    501 	} else if (client->state == RFB_SECURITY_TYPE) {
    502 		set_env("RFB_STATE", "SECURITY_TYPE");
    503 	} else if (client->state == RFB_AUTHENTICATION) {
    504 		set_env("RFB_STATE", "AUTHENTICATION");
    505 	} else if (client->state == RFB_INITIALISATION) {
    506 		set_env("RFB_STATE", "INITIALISATION");
    507 	} else if (client->state == RFB_NORMAL) {
    508 		set_env("RFB_STATE", "NORMAL");
    509 	} else {
    510 		set_env("RFB_STATE", "UNKNOWN");
    511 	}
    512 	if (certret_str) {
    513 		set_env("RFB_SSL_CLIENT_CERT", certret_str);
    514 	} else {
    515 		set_env("RFB_SSL_CLIENT_CERT", "");
    516 	}
    517 
    518 	/* set RFB_CLIENT_PORT to peer port for command to use */
    519 	if (cd && cd->client_port > 0) {
    520 		sprintf(str, "%d", cd->client_port);
    521 	} else if (client) {
    522 		sprintf(str, "%d", get_remote_port(client->sock));
    523 	}
    524 	set_env("RFB_CLIENT_PORT", str);
    525 
    526 	set_env("RFB_MODE", mode);
    527 
    528 	/*
    529 	 * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!)
    530 	 * This will establish a 5-tuple (including tcp) the external
    531 	 * program can potentially use to work out the virtual circuit
    532 	 * for this connection.
    533 	 */
    534 	if (cd && cd->server_ip) {
    535 		set_env("RFB_SERVER_IP", cd->server_ip);
    536 	} else if (client) {
    537 		char *sip = get_local_host(client->sock);
    538 		set_env("RFB_SERVER_IP", sip);
    539 		if (sip) free(sip);
    540 	}
    541 
    542 	if (cd && cd->server_port > 0) {
    543 		sprintf(str, "%d", cd->server_port);
    544 	} else if (client) {
    545 		sprintf(str, "%d", get_local_port(client->sock));
    546 	}
    547 	set_env("RFB_SERVER_PORT", str);
    548 
    549 	if (cd) {
    550 		sprintf(str, "%d", cd->login_viewonly);
    551 	} else {
    552 		sprintf(str, "%d", -1);
    553 	}
    554 	set_env("RFB_LOGIN_VIEWONLY", str);
    555 
    556 	if (cd) {
    557 		sprintf(str, "%d", (int) cd->login_time);
    558 	} else {
    559 		sprintf(str, ">%d", (int) time(NULL));
    560 	}
    561 	set_env("RFB_LOGIN_TIME", str);
    562 
    563 	sprintf(str, "%d", (int) time(NULL));
    564 	set_env("RFB_CURRENT_TIME", str);
    565 
    566 	if (!cd || !cd->username || cd->username[0] == '\0') {
    567 		set_env("RFB_USERNAME", "unknown-user");
    568 	} else {
    569 		set_env("RFB_USERNAME", cd->username);
    570 	}
    571 	/*
    572 	 * Better set DISPLAY to the one we are polling, if they
    573 	 * want something trickier, they can handle on their own
    574 	 * via environment, etc.
    575 	 */
    576 	if (getenv("DISPLAY")) {
    577 		old_display = strdup(getenv("DISPLAY"));
    578 	}
    579 
    580 	if (raw_fb && ! dpy) {	/* raw_fb hack */
    581 		set_env("DISPLAY", "rawfb");
    582 	} else {
    583 		set_env("DISPLAY", DisplayString(dpy));
    584 	}
    585 
    586 	/*
    587 	 * work out the number of clients (have to use client_count
    588 	 * since there is deadlock in rfbGetClientIterator)
    589 	 */
    590 	sprintf(str, "%d", client_count);
    591 	set_env("RFB_CLIENT_COUNT", str);
    592 
    593 	/* gone, accept, afteraccept */
    594 	ok = 0;
    595 	if (!strcmp(mode, "env")) {
    596 		return 1;
    597 	}
    598 	if (!strcmp(mode, "accept") && cmd_ok("accept")) {
    599 		ok = 1;
    600 	}
    601 	if (!strcmp(mode, "afteraccept") && cmd_ok("afteraccept")) {
    602 		ok = 1;
    603 	}
    604 	if (!strcmp(mode, "gone") && cmd_ok("gone")) {
    605 		ok = 1;
    606 	}
    607 	if (!strcmp(mode, "cmd_verify") && cmd_ok("unixpw")) {
    608 		ok = 1;
    609 	}
    610 	if (!strcmp(mode, "read_passwds") && cmd_ok("passwdfile")) {
    611 		ok = 1;
    612 	}
    613 	if (!strcmp(mode, "custom_passwd") && cmd_ok("custom_passwd")) {
    614 		ok = 1;
    615 	}
    616 	if (no_external_cmds || !ok) {
    617 		rfbLogEnable(1);
    618 		rfbLog("cannot run external commands in -nocmds mode:\n");
    619 		rfbLog("   \"%s\"\n", cmd);
    620 		rfbLog("   exiting.\n");
    621 		clean_up_exit(1);
    622 	}
    623 	rfbLog("running command:\n");
    624 	if (!quiet) {
    625 		fprintf(stderr, "\n  %s\n\n", cmd);
    626 	}
    627 	close_exec_fds();
    628 
    629 	if (output != NULL) {
    630 		FILE *ph;
    631 		char line[1024];
    632 		char *cmd2 = NULL;
    633 		char tmp[] = "/tmp/x11vnc-tmp.XXXXXX";
    634 		int deltmp = 0;
    635 
    636 		if (input != NULL) {
    637 			int tmp_fd = mkstemp(tmp);
    638 			if (tmp_fd < 0) {
    639 				rfbLog("mkstemp failed on: %s\n", tmp);
    640 				clean_up_exit(1);
    641 			}
    642 			write(tmp_fd, input, len);
    643 			close(tmp_fd);
    644 			deltmp = 1;
    645 			cmd2 = (char *) malloc(100 + strlen(tmp) + strlen(cmd));
    646 			sprintf(cmd2, "/bin/cat %s | %s", tmp, cmd);
    647 
    648 			ph = popen(cmd2, "r");
    649 		} else {
    650 			ph = popen(cmd, "r");
    651 		}
    652 		if (ph == NULL) {
    653 			rfbLog("popen(%s) failed", cmd);
    654 			rfbLogPerror("popen");
    655 			clean_up_exit(1);
    656 		}
    657 		memset(line, 0, sizeof(line));
    658 		while (fgets(line, sizeof(line), ph) != NULL) {
    659 			int j, k = -1;
    660 			if (0) fprintf(stderr, "line: %s", line);
    661 			/* take care to handle embedded nulls */
    662 			for (j=0; j < (int) sizeof(line); j++) {
    663 				if (line[j] != '\0') {
    664 					k = j;
    665 				}
    666 			}
    667 			if (k >= 0) {
    668 				write(fileno(output), line, k+1);
    669 			}
    670 			memset(line, 0, sizeof(line));
    671 		}
    672 
    673 		rc = pclose(ph);
    674 
    675 		if (cmd2 != NULL) {
    676 			free(cmd2);
    677 		}
    678 		if (deltmp) {
    679 			unlink(tmp);
    680 		}
    681 		goto got_rc;
    682 	} else if (input != NULL) {
    683 		FILE *ph = popen(cmd, "w");
    684 		if (ph == NULL) {
    685 			rfbLog("popen(%s) failed", cmd);
    686 			rfbLogPerror("popen");
    687 			clean_up_exit(1);
    688 		}
    689 		write(fileno(ph), input, len);
    690 		rc = pclose(ph);
    691 		goto got_rc;
    692 	}
    693 
    694 #if LIBVNCSERVER_HAVE_FORK
    695 	{
    696 		pid_t pid, pidw;
    697 		struct sigaction sa, intr, quit;
    698 		sigset_t omask;
    699 
    700 		sa.sa_handler = SIG_IGN;
    701 		sa.sa_flags = 0;
    702 		sigemptyset(&sa.sa_mask);
    703 		sigaction(SIGINT,  &sa, &intr);
    704 		sigaction(SIGQUIT, &sa, &quit);
    705 
    706 		sigaddset(&sa.sa_mask, SIGCHLD);
    707 		sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);
    708 
    709 		if ((pid = fork()) > 0 || pid == -1) {
    710 
    711 			if (pid != -1) {
    712 				pidw = waitpid(pid, &rc, 0);
    713 			}
    714 
    715 			sigaction(SIGINT,  &intr, (struct sigaction *) NULL);
    716 			sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
    717 			sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
    718 
    719 			if (pid == -1) {
    720 				fprintf(stderr, "could not fork\n");
    721 				rfbLogPerror("fork");
    722 				rc = system(cmd);
    723 			}
    724 		} else {
    725 			/* this should close port 5900, etc.. */
    726 			int fd;
    727 			sigaction(SIGINT,  &intr, (struct sigaction *) NULL);
    728 			sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
    729 			sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
    730 			for (fd=3; fd<256; fd++) {
    731 				close(fd);
    732 			}
    733 /* XXX test more */
    734 			if (!strcmp(mode, "gone")) {
    735 #if LIBVNCSERVER_HAVE_SETSID
    736 				setsid();
    737 #else
    738 				setpgrp();
    739 #endif
    740 			}
    741 			execlp("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
    742 			exit(1);
    743 		}
    744 	}
    745 #else
    746 	rc = system(cmd);
    747 #endif
    748 	got_rc:
    749 
    750 	if (rc >= 256) {
    751 		rc = rc/256;
    752 	}
    753 	rfbLog("command returned: %d\n", rc);
    754 
    755 	if (old_display) {
    756 		set_env("DISPLAY", old_display);
    757 		free(old_display);
    758 	}
    759 
    760 	return rc;
    761 }
    762 
    763 static void free_client_data(rfbClientPtr client) {
    764 	if (! client) {
    765 		return;
    766 	}
    767 	if (client->clientData) {
    768 		ClientData *cd = (ClientData *) client->clientData;
    769 		if (cd) {
    770 			if (cd->server_ip) {
    771 				free(cd->server_ip);
    772 				cd->server_ip = NULL;
    773 			}
    774 			if (cd->hostname) {
    775 				free(cd->hostname);
    776 				cd->hostname = NULL;
    777 			}
    778 			if (cd->username) {
    779 				free(cd->username);
    780 				cd->username = NULL;
    781 			}
    782 			if (cd->unixname) {
    783 				free(cd->unixname);
    784 				cd->unixname = NULL;
    785 			}
    786 		}
    787 		free(client->clientData);
    788 		client->clientData = NULL;
    789 	}
    790 }
    791 
    792 static int accepted_client = 0;
    793 
    794 /*
    795  * callback for when a client disconnects
    796  */
    797 void client_gone(rfbClientPtr client) {
    798 	ClientData *cd = NULL;
    799 
    800 	CLIENT_LOCK;
    801 
    802 	client_count--;
    803 	if (client_count < 0) client_count = 0;
    804 
    805 	speeds_net_rate_measured = 0;
    806 	speeds_net_latency_measured = 0;
    807 
    808 	rfbLog("client_count: %d\n", client_count);
    809 	last_client_gone = dnow();
    810 
    811 	if (unixpw_in_progress && unixpw_client) {
    812 		if (client == unixpw_client) {
    813 			unixpw_in_progress = 0;
    814 			/* mutex */
    815 			screen->permitFileTransfer = unixpw_file_xfer_save;
    816 			if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
    817 #ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
    818 				rfbLog("rfbRegisterTightVNCFileTransferExtension: 3\n");
    819 				rfbRegisterTightVNCFileTransferExtension();
    820 #endif
    821 			}
    822 			unixpw_client = NULL;
    823 			copy_screen();
    824 		}
    825 	}
    826 
    827 
    828 	if (no_autorepeat && client_count == 0) {
    829 		autorepeat(1, 0);
    830 	}
    831 	if (use_solid_bg && client_count == 0) {
    832 		solid_bg(1);
    833 	}
    834 	if ((ncache || ncache0) && client_count == 0) {
    835 		kde_no_animate(1);
    836 	}
    837 	if (client->clientData) {
    838 		cd = (ClientData *) client->clientData;
    839 		if (cd->ssl_helper_pid > 0) {
    840 			int status;
    841 			rfbLog("sending SIGTERM to ssl_helper_pid: %d\n",
    842 			    cd->ssl_helper_pid);
    843 			kill(cd->ssl_helper_pid, SIGTERM);
    844 			usleep(200*1000);
    845 #if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
    846 			waitpid(cd->ssl_helper_pid, &status, WNOHANG);
    847 #endif
    848 			ssl_helper_pid(cd->ssl_helper_pid, -1);	/* delete */
    849 		}
    850 	}
    851 	if (gone_cmd && *gone_cmd != '\0') {
    852 		if (strstr(gone_cmd, "popup") == gone_cmd) {
    853 			int x = -64000, y = -64000, timeout = 120;
    854 			char *userhost = ident_username(client);
    855 			char *addr, *p, *mode;
    856 
    857 			/* extract timeout */
    858 			if ((p = strchr(gone_cmd, ':')) != NULL) {
    859 				int in;
    860 				if (sscanf(p+1, "%d", &in) == 1) {
    861 					timeout = in;
    862 				}
    863 			}
    864 			/* extract geometry */
    865 			if ((p = strpbrk(gone_cmd, "+-")) != NULL) {
    866 				ugly_geom(p, &x, &y);
    867 			}
    868 
    869 			/* find mode: mouse, key, or both */
    870 			if (strstr(gone_cmd, "popupmouse") == gone_cmd) {
    871 				mode = "mouse_only";
    872 			} else if (strstr(gone_cmd, "popupkey") == gone_cmd) {
    873 				mode = "key_only";
    874 			} else {
    875 				mode = "both";
    876 			}
    877 
    878 			addr = client->host;
    879 
    880 			ugly_window(addr, userhost, x, y, timeout, mode, 0);
    881 
    882 			free(userhost);
    883 		} else {
    884 			rfbLog("client_gone: using cmd: %s\n", client->host);
    885 			run_user_command(gone_cmd, client, "gone", NULL, 0, NULL);
    886 		}
    887 	}
    888 
    889 	free_client_data(client);
    890 
    891 	if (inetd && client == inetd_client) {
    892 		rfbLog("inetd viewer exited.\n");
    893 		if (gui_pid > 0) {
    894 			rfbLog("killing gui_pid %d\n", gui_pid);
    895 			kill(gui_pid, SIGTERM);
    896 		}
    897 		clean_up_exit(0);
    898 	}
    899 
    900 	if (connect_once) {
    901 		/*
    902 		 * This non-exit is done for a bad passwd to be consistent
    903 		 * with our RFB_CLIENT_REFUSE behavior in new_client()  (i.e.
    904 		 * we disconnect after 1 successful connection).
    905 		 */
    906 		if ((client->state == RFB_PROTOCOL_VERSION ||
    907 		     client->state == RFB_SECURITY_TYPE ||
    908 		     client->state == RFB_AUTHENTICATION ||
    909 		     client->state == RFB_INITIALISATION) && accepted_client) {
    910 			rfbLog("connect_once: invalid password or early "
    911 			   "disconnect.  %d\n", client->state);
    912 			rfbLog("connect_once: waiting for next connection.\n");
    913 			accepted_client--;
    914 			if (accepted_client < 0) {
    915 				accepted_client = 0;
    916 			}
    917 			CLIENT_UNLOCK;
    918 			if (connect_or_exit) {
    919 				clean_up_exit(1);
    920 			}
    921 			return;
    922 		}
    923 		if (shared && client_count > 0)  {
    924 			rfbLog("connect_once: other shared clients still "
    925 			    "connected, not exiting.\n");
    926 			CLIENT_UNLOCK;
    927 			return;
    928 		}
    929 
    930 		rfbLog("viewer exited.\n");
    931 		if ((client_connect || connect_or_exit) && gui_pid > 0) {
    932 			rfbLog("killing gui_pid %d\n", gui_pid);
    933 			kill(gui_pid, SIGTERM);
    934 		}
    935 		CLIENT_UNLOCK;
    936 		clean_up_exit(0);
    937 	}
    938 #ifdef MACOSX
    939 	if (macosx_console && client_count == 0) {
    940 		macosxCG_refresh_callback_off();
    941 	}
    942 #endif
    943 	CLIENT_UNLOCK;
    944 }
    945 
    946 /*
    947  * Simple routine to limit access via string compare.  A power user will
    948  * want to compile libvncserver with libwrap support and use /etc/hosts.allow.
    949  */
    950 int check_access(char *addr) {
    951 	int allowed = 0;
    952 	int ssl = 0;
    953 	char *p, *list;
    954 
    955 	if (use_openssl || use_stunnel) {
    956 		ssl = 1;
    957 	}
    958 	if (deny_all) {
    959 		rfbLog("check_access: new connections are currently "
    960 		    "blocked.\n");
    961 		return 0;
    962 	}
    963 	if (addr == NULL || *addr == '\0') {
    964 		rfbLog("check_access: denying empty host IP address string.\n");
    965 		return 0;
    966 	}
    967 
    968 	if (allow_list == NULL) {
    969 		/* set to "" to possibly append allow_once */
    970 		allow_list = strdup("");
    971 	}
    972 	if (*allow_list == '\0' && allow_once == NULL) {
    973 		/* no constraints, accept it */
    974 		return 1;
    975 	}
    976 
    977 	if (strchr(allow_list, '/')) {
    978 		/* a file of IP addresess or prefixes */
    979 		int len, len2 = 0;
    980 		struct stat sbuf;
    981 		FILE *in;
    982 		char line[1024], *q;
    983 
    984 		if (stat(allow_list, &sbuf) != 0) {
    985 			rfbLogEnable(1);
    986 			rfbLog("check_access: failure stating file: %s\n",
    987 			    allow_list);
    988 			rfbLogPerror("stat");
    989 			clean_up_exit(1);
    990 		}
    991 		len = sbuf.st_size + 1;	/* 1 more for '\0' at end */
    992 		if (allow_once) {
    993 			len2 = strlen(allow_once) + 2;
    994 			len += len2;
    995 		}
    996 		if (ssl) {
    997 			len2 = strlen("127.0.0.1") + 2;
    998 			len += len2;
    999 		}
   1000 		list = (char *) malloc(len);
   1001 		list[0] = '\0';
   1002 
   1003 		in = fopen(allow_list, "r");
   1004 		if (in == NULL) {
   1005 			rfbLogEnable(1);
   1006 			rfbLog("check_access: cannot open: %s\n", allow_list);
   1007 			rfbLogPerror("fopen");
   1008 			clean_up_exit(1);
   1009 		}
   1010 		while (fgets(line, 1024, in) != NULL) {
   1011 			if ( (q = strchr(line, '#')) != NULL) {
   1012 				*q = '\0';
   1013 			}
   1014 			if (strlen(list) + strlen(line) >=
   1015 			    (size_t) (len - len2)) {
   1016 				/* file grew since our stat() */
   1017 				break;
   1018 			}
   1019 			strcat(list, line);
   1020 		}
   1021 		fclose(in);
   1022 		if (allow_once) {
   1023 			strcat(list, "\n");
   1024 			strcat(list, allow_once);
   1025 			strcat(list, "\n");
   1026 		}
   1027 		if (ssl) {
   1028 			strcat(list, "\n");
   1029 			strcat(list, "127.0.0.1");
   1030 			strcat(list, "\n");
   1031 		}
   1032 	} else {
   1033 		int len = strlen(allow_list) + 1;
   1034 		if (allow_once) {
   1035 			len += strlen(allow_once) + 1;
   1036 		}
   1037 		if (ssl) {
   1038 			len += strlen("127.0.0.1") + 1;
   1039 		}
   1040 		list = (char *) malloc(len);
   1041 		list[0] = '\0';
   1042 		strcat(list, allow_list);
   1043 		if (allow_once) {
   1044 			strcat(list, ",");
   1045 			strcat(list, allow_once);
   1046 		}
   1047 		if (ssl) {
   1048 			strcat(list, ",");
   1049 			strcat(list, "127.0.0.1");
   1050 		}
   1051 	}
   1052 
   1053 	if (allow_once) {
   1054 		free(allow_once);
   1055 		allow_once = NULL;
   1056 	}
   1057 
   1058 	p = strtok(list, ", \t\n\r");
   1059 	while (p) {
   1060 		char *chk, *q, *r = NULL;
   1061 		if (*p == '\0') {
   1062 			p = strtok(NULL, ", \t\n\r");
   1063 			continue;
   1064 		}
   1065 		if (ipv6_ip(p)) {
   1066 			chk = p;
   1067 		} else if (! dotted_ip(p, 1)) {
   1068 			r = host2ip(p);
   1069 			if (r == NULL || *r == '\0') {
   1070 				rfbLog("check_access: bad lookup \"%s\"\n", p);
   1071 				p = strtok(NULL, ", \t\n\r");
   1072 				continue;
   1073 			}
   1074 			rfbLog("check_access: lookup %s -> %s\n", p, r);
   1075 			chk = r;
   1076 		} else {
   1077 			chk = p;
   1078 		}
   1079 		if (getenv("X11VNC_DEBUG_ACCESS")) fprintf(stderr, "chk: %s  part: %s  addr: %s\n", chk, p, addr);
   1080 
   1081 		q = strstr(addr, chk);
   1082 		if (ipv6_ip(addr)) {
   1083 			if (!strcmp(chk, "localhost") && !strcmp(addr, "::1")) {
   1084 				rfbLog("check_access: client addr %s is local.\n", addr);
   1085 				allowed = 1;
   1086 			} else if (!strcmp(chk, "::1") && !strcmp(addr, "::1")) {
   1087 				rfbLog("check_access: client addr %s is local.\n", addr);
   1088 				allowed = 1;
   1089 			} else if (!strcmp(chk, "127.0.0.1") && !strcmp(addr, "::1")) {
   1090 				/* this if for host2ip("localhost") */
   1091 				rfbLog("check_access: client addr %s is local.\n", addr);
   1092 				allowed = 1;
   1093 			} else if (q == addr) {
   1094 				rfbLog("check_access: client %s matches pattern %s\n", addr, chk);
   1095 				allowed = 1;
   1096 			}
   1097 		} else if (chk[strlen(chk)-1] != '.') {
   1098 			if (!strcmp(addr, chk)) {
   1099 				if (chk != p) {
   1100 					rfbLog("check_access: client %s " "matches host %s=%s\n", addr, chk, p);
   1101 				} else {
   1102 					rfbLog("check_access: client %s " "matches host %s\n", addr, chk);
   1103 				}
   1104 				allowed = 1;
   1105 			} else if(!strcmp(chk, "localhost") && !strcmp(addr, "127.0.0.1")) {
   1106 				allowed = 1;
   1107 			}
   1108 		} else if (q == addr) {
   1109 			rfbLog("check_access: client %s matches pattern %s\n", addr, chk);
   1110 			allowed = 1;
   1111 		}
   1112 		p = strtok(NULL, ", \t\n\r");
   1113 		if (r) {
   1114 			free(r);
   1115 		}
   1116 		if (allowed) {
   1117 			break;
   1118 		}
   1119 	}
   1120 	free(list);
   1121 	return allowed;
   1122 }
   1123 
   1124 /*
   1125  * x11vnc's first (and only) visible widget: accept/reject dialog window.
   1126  * We go through this pain to avoid dependency on libXt...
   1127  */
   1128 static int ugly_window(char *addr, char *userhost, int X, int Y,
   1129     int timeout, char *mode, int accept) {
   1130 #if NO_X11
   1131 	if (!addr || !userhost || !X || !Y || !timeout || !mode || !accept) {}
   1132 	RAWFB_RET(0)
   1133 	nox11_exit(1);
   1134 	return 0;
   1135 #else
   1136 
   1137 #define t2x2_width 16
   1138 #define t2x2_height 16
   1139 static unsigned char t2x2_bits[] = {
   1140    0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff,
   1141    0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33,
   1142    0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33};
   1143 
   1144 	Window awin;
   1145 	GC gc;
   1146 	XSizeHints hints;
   1147 	XGCValues values;
   1148 	static XFontStruct *font_info = NULL;
   1149 	static Pixmap ico = 0;
   1150 	unsigned long valuemask = 0;
   1151 	static char dash_list[] = {20, 40};
   1152 	int list_length = sizeof(dash_list);
   1153 
   1154 	Atom wm_protocols;
   1155 	Atom wm_delete_window;
   1156 
   1157 	XEvent ev;
   1158 	long evmask = ExposureMask | KeyPressMask | ButtonPressMask
   1159 	    | StructureNotifyMask;
   1160 	double waited = 0.0;
   1161 
   1162 	/* strings and geometries y/n */
   1163 	KeyCode key_y, key_n, key_v;
   1164 	char strh[100];
   1165 	char stri[100];
   1166 	char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button";
   1167 	char str2_b[] = "To reject: press \"n\" or click the \"No\" button";
   1168 	char str3_b[] = "View only: press \"v\" or click the \"View\" button";
   1169 	char str1_m[] = "To accept: click the \"Yes\" button";
   1170 	char str2_m[] = "To reject: click the \"No\" button";
   1171 	char str3_m[] = "View only: click the \"View\" button";
   1172 	char str1_k[] = "To accept: press \"y\"";
   1173 	char str2_k[] = "To reject: press \"n\"";
   1174 	char str3_k[] = "View only: press \"v\"";
   1175 	char *str1, *str2, *str3;
   1176 	char str_y[] = "Yes";
   1177 	char str_n[] = "No";
   1178 	char str_v[] = "View";
   1179 	int x, y, w = 345, h = 175, ret = 0;
   1180 	int X_sh = 20, Y_sh = 30, dY = 20;
   1181 	int Ye_x = 20,  Ye_y = 0, Ye_w = 45, Ye_h = 20;
   1182 	int No_x = 75,  No_y = 0, No_w = 45, No_h = 20;
   1183 	int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20;
   1184 	char *sprop = "new x11vnc client";
   1185 
   1186 	KeyCode key_o;
   1187 
   1188 	RAWFB_RET(0)
   1189 
   1190 	if (! accept) {
   1191 		sprintf(str_y, "OK");
   1192 		sprop = "x11vnc client disconnected";
   1193 		h = 110;
   1194 		str1 = "";
   1195 		str2 = "";
   1196 		str3 = "";
   1197 	} else if (!strcmp(mode, "mouse_only")) {
   1198 		str1 = str1_m;
   1199 		str2 = str2_m;
   1200 		str3 = str3_m;
   1201 	} else if (!strcmp(mode, "key_only")) {
   1202 		str1 = str1_k;
   1203 		str2 = str2_k;
   1204 		str3 = str3_k;
   1205 		h -= dY;
   1206 	} else {
   1207 		str1 = str1_b;
   1208 		str2 = str2_b;
   1209 		str3 = str3_b;
   1210 	}
   1211 	if (view_only) {
   1212 		h -= dY;
   1213 	}
   1214 
   1215 	/* XXX handle coff_x/coff_y? */
   1216 	if (X < -dpy_x) {
   1217 		x = (dpy_x - w)/2;	/* large negative: center */
   1218 		if (x < 0) x = 0;
   1219 	} else if (X < 0) {
   1220 		x = dpy_x + X - w;	/* from lower right */
   1221 	} else {
   1222 		x = X;			/* from upper left */
   1223 	}
   1224 
   1225 	if (Y < -dpy_y) {
   1226 		y = (dpy_y - h)/2;
   1227 		if (y < 0) y = 0;
   1228 	} else if (Y < 0) {
   1229 		y = dpy_y + Y - h;
   1230 	} else {
   1231 		y = Y;
   1232 	}
   1233 
   1234 	X_LOCK;
   1235 
   1236 	awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4,
   1237 	    BlackPixel(dpy, scr), WhitePixel(dpy, scr));
   1238 
   1239 	wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
   1240 	wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
   1241 	XSetWMProtocols(dpy, awin, &wm_delete_window, 1);
   1242 
   1243 	if (! ico) {
   1244 		ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits,
   1245 		    t2x2_width, t2x2_height);
   1246 	}
   1247 
   1248 	hints.flags = PPosition | PSize | PMinSize;
   1249 	hints.x = x;
   1250 	hints.y = y;
   1251 	hints.width = w;
   1252 	hints.height = h;
   1253 	hints.min_width = w;
   1254 	hints.min_height = h;
   1255 
   1256 	XSetStandardProperties(dpy, awin, sprop, "x11vnc query", ico, NULL,
   1257 	    0, &hints);
   1258 
   1259 	XSelectInput_wr(dpy, awin, evmask);
   1260 
   1261 	if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) {
   1262 		rfbLogEnable(1);
   1263 		rfbLog("ugly_window: cannot locate font fixed.\n");
   1264 		X_UNLOCK;
   1265 		clean_up_exit(1);
   1266 	}
   1267 
   1268 	gc = XCreateGC(dpy, awin, valuemask, &values);
   1269 	XSetFont(dpy, gc, font_info->fid);
   1270 	XSetForeground(dpy, gc, BlackPixel(dpy, scr));
   1271 	XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
   1272 	XSetDashes(dpy, gc, 0, dash_list, list_length);
   1273 
   1274 	XMapWindow(dpy, awin);
   1275 	XFlush_wr(dpy);
   1276 
   1277 	if (accept) {
   1278 		char *ip = addr;
   1279 		char *type = "accept";
   1280 		if (unixpw && strstr(userhost, "UNIX:") != userhost) {
   1281 			type = "UNIXPW";
   1282 			if (openssl_last_ip) {
   1283 				ip = openssl_last_ip;
   1284 			}
   1285 		}
   1286 		snprintf(strh, 100, "x11vnc: %s connection from %s?", type, ip);
   1287 	} else {
   1288 		snprintf(strh, 100, "x11vnc: client disconnected from %s", addr);
   1289 	}
   1290 	snprintf(stri, 100, "        (%s)", userhost);
   1291 
   1292 	key_o = XKeysymToKeycode(dpy, XStringToKeysym("o"));
   1293 	key_y = XKeysymToKeycode(dpy, XStringToKeysym("y"));
   1294 	key_n = XKeysymToKeycode(dpy, XStringToKeysym("n"));
   1295 	key_v = XKeysymToKeycode(dpy, XStringToKeysym("v"));
   1296 
   1297 	while (1) {
   1298 		int out = -1, x, y, tw, k;
   1299 
   1300 		if (XCheckWindowEvent(dpy, awin, evmask, &ev)) {
   1301 			;	/* proceed to handling */
   1302 		} else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) {
   1303 			;	/* proceed to handling */
   1304 		} else {
   1305 			int ms = 100;	/* sleep a bit */
   1306 			usleep(ms * 1000);
   1307 			waited += ((double) ms)/1000.;
   1308 			if (timeout && (int) waited >= timeout) {
   1309 				rfbLog("ugly_window: popup timed out after "
   1310 				    "%d seconds.\n", timeout);
   1311 				out = 0;
   1312 				ev.type = 0;
   1313 			} else {
   1314 				continue;
   1315 			}
   1316 		}
   1317 
   1318 		switch(ev.type) {
   1319 		case Expose:
   1320 			while (XCheckTypedEvent(dpy, Expose, &ev)) {
   1321 				;
   1322 			}
   1323 			k=0;
   1324 
   1325 			/* instructions */
   1326 			XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
   1327 			    strh, strlen(strh));
   1328 			XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
   1329 			    stri, strlen(stri));
   1330 			if (accept) {
   1331 			  XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
   1332 			    str1, strlen(str1));
   1333 			  XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
   1334 			    str2, strlen(str2));
   1335 			  if (! view_only) {
   1336 				XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
   1337 				    str3, strlen(str3));
   1338 			  }
   1339 			}
   1340 
   1341 			if (!strcmp(mode, "key_only")) {
   1342 				break;
   1343 			}
   1344 
   1345 			/* buttons */
   1346 			Ye_y = Y_sh+k*dY;
   1347 			No_y = Y_sh+k*dY;
   1348 			Vi_y = Y_sh+k*dY;
   1349 			XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h);
   1350 
   1351 			if (accept) {
   1352 			  XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h);
   1353 			  if (! view_only) {
   1354 				XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y,
   1355 				    Vi_w, Vi_h);
   1356 			  }
   1357 			}
   1358 
   1359 			tw = XTextWidth(font_info, str_y, strlen(str_y));
   1360 			tw = (Ye_w - tw)/2;
   1361 			if (tw < 0) tw = 1;
   1362 			XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5,
   1363 			    str_y, strlen(str_y));
   1364 
   1365 			if (!accept) {
   1366 				break;
   1367 			}
   1368 			tw = XTextWidth(font_info, str_n, strlen(str_n));
   1369 			tw = (No_w - tw)/2;
   1370 			if (tw < 0) tw = 1;
   1371 			XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5,
   1372 			    str_n, strlen(str_n));
   1373 
   1374 			if (! view_only) {
   1375 				tw = XTextWidth(font_info, str_v,
   1376 				    strlen(str_v));
   1377 				tw = (Vi_w - tw)/2;
   1378 				if (tw < 0) tw = 1;
   1379 				XDrawString(dpy, awin, gc, Vi_x+tw,
   1380 				    Vi_y+Vi_h-5, str_v, strlen(str_v));
   1381 			}
   1382 
   1383 			break;
   1384 
   1385 		case ClientMessage:
   1386 			if (ev.xclient.message_type == wm_protocols &&
   1387 			    (Atom) ev.xclient.data.l[0] == wm_delete_window) {
   1388 				out = 0;
   1389 			}
   1390 			break;
   1391 
   1392 		case ButtonPress:
   1393 			x = ev.xbutton.x;
   1394 			y = ev.xbutton.y;
   1395 			if (!strcmp(mode, "key_only")) {
   1396 				;
   1397 			} else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y
   1398 			    && y < Ye_y+Ye_h) {
   1399 				out = 1;
   1400 			} else if (! accept) {
   1401 				;
   1402 			} else if (x > No_x && x < No_x+No_w && y > No_y
   1403 			    && y < No_y+No_h) {
   1404 				out = 0;
   1405 			} else if (! view_only && x > Vi_x && x < Vi_x+Vi_w
   1406 			    && y > Vi_y && y < Vi_y+Ye_h) {
   1407 				out = 2;
   1408 			}
   1409 			break;
   1410 
   1411 		case KeyPress:
   1412 			if (!strcmp(mode, "mouse_only")) {
   1413 				;
   1414 			} else if (! accept) {
   1415 				if (ev.xkey.keycode == key_o) {
   1416 					out = 1;
   1417 				}
   1418 				if (ev.xkey.keycode == key_y) {
   1419 					out = 1;
   1420 				}
   1421 			} else if (ev.xkey.keycode == key_y) {
   1422 				out = 1;
   1423 				;
   1424 			} else if (ev.xkey.keycode == key_n) {
   1425 				out = 0;
   1426 			} else if (! view_only && ev.xkey.keycode == key_v) {
   1427 				out = 2;
   1428 			}
   1429 			break;
   1430 		default:
   1431 			break;
   1432 		}
   1433 		if (out != -1) {
   1434 			ret = out;
   1435 			XSelectInput_wr(dpy, awin, 0);
   1436 			XUnmapWindow(dpy, awin);
   1437 			XFree_wr(gc);
   1438 			XDestroyWindow(dpy, awin);
   1439 			XFlush_wr(dpy);
   1440 			break;
   1441 		}
   1442 	}
   1443 	X_UNLOCK;
   1444 
   1445 	return ret;
   1446 #endif	/* NO_X11 */
   1447 }
   1448 
   1449 /*
   1450  * process a "yes:0,no:*,view:3" type action list comparing to command
   1451  * return code rc.  * means the default action with no other match.
   1452  */
   1453 static int action_match(char *action, int rc) {
   1454 	char *p, *q, *s = strdup(action);
   1455 	int cases[4], i, result;
   1456 	char *labels[4];
   1457 
   1458 	labels[1] = "yes";
   1459 	labels[2] = "no";
   1460 	labels[3] = "view";
   1461 
   1462 	rfbLog("accept_client: process action line: %s\n",
   1463 	    action);
   1464 
   1465 	for (i=1; i <= 3; i++) {
   1466 		cases[i] = -2;
   1467 	}
   1468 
   1469 	p = strtok(s, ",");
   1470 	while (p) {
   1471 		if ((q = strchr(p, ':')) != NULL) {
   1472 			int in, k = 1;
   1473 			*q = '\0';
   1474 			q++;
   1475 			if (strstr(p, "yes") == p) {
   1476 				k = 1;
   1477 			} else if (strstr(p, "no") == p) {
   1478 				k = 2;
   1479 			} else if (strstr(p, "view") == p) {
   1480 				k = 3;
   1481 			} else {
   1482 				rfbLogEnable(1);
   1483 				rfbLog("invalid action line: %s\n", action);
   1484 				clean_up_exit(1);
   1485 			}
   1486 			if (*q == '*') {
   1487 				cases[k] = -1;
   1488 			} else if (sscanf(q, "%d", &in) == 1) {
   1489 				if (in < 0) {
   1490 					rfbLogEnable(1);
   1491 					rfbLog("invalid action line: %s\n",
   1492 					    action);
   1493 					clean_up_exit(1);
   1494 				}
   1495 				cases[k] = in;
   1496 			} else {
   1497 				rfbLogEnable(1);
   1498 				rfbLog("invalid action line: %s\n", action);
   1499 				clean_up_exit(1);
   1500 			}
   1501 		} else {
   1502 			rfbLogEnable(1);
   1503 			rfbLog("invalid action line: %s\n", action);
   1504 			clean_up_exit(1);
   1505 		}
   1506 		p = strtok(NULL, ",");
   1507 	}
   1508 	free(s);
   1509 
   1510 	result = -1;
   1511 	for (i=1; i <= 3; i++) {
   1512 		if (cases[i] == -1) {
   1513 			rfbLog("accept_client: default action is case=%d %s\n",
   1514 			    i, labels[i]);
   1515 			result = i;
   1516 			break;
   1517 		}
   1518 	}
   1519 	if (result == -1) {
   1520 		rfbLog("accept_client: no default action\n");
   1521 	}
   1522 	for (i=1; i <= 3; i++) {
   1523 		if (cases[i] >= 0 && cases[i] == rc) {
   1524 			rfbLog("accept_client: matched action is case=%d %s\n",
   1525 			    i, labels[i]);
   1526 			result = i;
   1527 			break;
   1528 		}
   1529 	}
   1530 	if (result < 0) {
   1531 		rfbLog("no action match: %s rc=%d set to no\n", action, rc);
   1532 		result = 2;
   1533 	}
   1534 	return result;
   1535 }
   1536 
   1537 static void ugly_geom(char *p, int *x, int *y) {
   1538 	int x1, y1;
   1539 
   1540 	if (sscanf(p, "+%d+%d", &x1, &y1) == 2) {
   1541 		*x = x1;
   1542 		*y = y1;
   1543 	} else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) {
   1544 		*x = x1;
   1545 		*y = -y1;
   1546 	} else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) {
   1547 		*x = -x1;
   1548 		*y = y1;
   1549 	} else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) {
   1550 		*x = -x1;
   1551 		*y = -y1;
   1552 	}
   1553 }
   1554 
   1555 /*
   1556  * Simple routine to prompt the user on the X display whether an incoming
   1557  * client should be allowed to connect or not.  If a gui is involved it
   1558  * will be running in the environment/context of the X11 DISPLAY.
   1559  *
   1560  * The command supplied via -accept is run as is (i.e. no string
   1561  * substitution) with the RFB_CLIENT_IP environment variable set to the
   1562  * incoming client's numerical IP address.
   1563  *
   1564  * If the external command exits with 0 the client is accepted, otherwise
   1565  * the client is rejected.
   1566  *
   1567  * Some builtins are provided:
   1568  *
   1569  *	xmessage:  use homebrew xmessage(1) for the external command.
   1570  *	popup:     use internal X widgets for prompting.
   1571  *
   1572  */
   1573 int accept_client(rfbClientPtr client) {
   1574 
   1575 	char xmessage[200], *cmd = NULL;
   1576 	char *addr = client->host;
   1577 	char *action = NULL;
   1578 
   1579 	if (accept_cmd == NULL || *accept_cmd == '\0') {
   1580 		return 1;	/* no command specified, so we accept */
   1581 	}
   1582 
   1583 	if (addr == NULL || addr[0] == '\0') {
   1584 		addr = "unknown-host";
   1585 	}
   1586 
   1587 	if (strstr(accept_cmd, "popup") == accept_cmd) {
   1588 		/* use our builtin popup button */
   1589 
   1590 		/* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */
   1591 
   1592 		int ret, timeout = 120;
   1593 		int x = -64000, y = -64000;
   1594 		char *p, *mode;
   1595 		char *userhost = ident_username(client);
   1596 
   1597 		/* extract timeout */
   1598 		if ((p = strchr(accept_cmd, ':')) != NULL) {
   1599 			int in;
   1600 			if (sscanf(p+1, "%d", &in) == 1) {
   1601 				timeout = in;
   1602 			}
   1603 		}
   1604 		/* extract geometry */
   1605 		if ((p = strpbrk(accept_cmd, "+-")) != NULL) {
   1606 			ugly_geom(p, &x, &y);
   1607 		}
   1608 
   1609 		/* find mode: mouse, key, or both */
   1610 		if (strstr(accept_cmd, "popupmouse") == accept_cmd) {
   1611 			mode = "mouse_only";
   1612 		} else if (strstr(accept_cmd, "popupkey") == accept_cmd) {
   1613 			mode = "key_only";
   1614 		} else {
   1615 			mode = "both";
   1616 		}
   1617 
   1618 		if (dpy == NULL && use_dpy && strstr(use_dpy, "WAIT:") ==
   1619 		    use_dpy) {
   1620 			rfbLog("accept_client: warning allowing client under conditions:\n");
   1621 			rfbLog("  -display WAIT:, dpy == NULL, -accept popup.\n");
   1622 			rfbLog("   There will be another popup.\n");
   1623 			return 1;
   1624 		}
   1625 
   1626 		rfbLog("accept_client: using builtin popup for: %s\n", addr);
   1627 		if ((ret = ugly_window(addr, userhost, x, y, timeout,
   1628 		    mode, 1))) {
   1629 			free(userhost);
   1630 			if (ret == 2) {
   1631 				rfbLog("accept_client: viewonly: %s\n", addr);
   1632 				client->viewOnly = TRUE;
   1633 			}
   1634 			rfbLog("accept_client: popup accepted: %s\n", addr);
   1635 			return 1;
   1636 		} else {
   1637 			free(userhost);
   1638 			rfbLog("accept_client: popup rejected: %s\n", addr);
   1639 			return 0;
   1640 		}
   1641 
   1642 	} else if (!strcmp(accept_cmd, "xmessage")) {
   1643 		/* make our own command using xmessage(1) */
   1644 
   1645 		if (view_only) {
   1646 			sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center"
   1647 			    " 'x11vnc: accept connection from %s?'", addr);
   1648 		} else {
   1649 			sprintf(xmessage, "xmessage -buttons yes:0,no:2,"
   1650 			    "view-only:3 -center" " 'x11vnc: accept connection"
   1651 			    " from %s?'", addr);
   1652 			action = "yes:0,no:*,view:3";
   1653 		}
   1654 		cmd = xmessage;
   1655 
   1656 	} else {
   1657 		/* use the user supplied command: */
   1658 
   1659 		cmd = accept_cmd;
   1660 
   1661 		/* extract any action prefix:  yes:N,no:M,view:K */
   1662 		if (strstr(accept_cmd, "yes:") == accept_cmd) {
   1663 			char *p;
   1664 			if ((p = strpbrk(accept_cmd, " \t")) != NULL) {
   1665 				int i;
   1666 				cmd = p;
   1667 				p = accept_cmd;
   1668 				for (i=0; i<200; i++) {
   1669 					if (*p == ' ' || *p == '\t') {
   1670 						xmessage[i] = '\0';
   1671 						break;
   1672 					}
   1673 					xmessage[i] = *p;
   1674 					p++;
   1675 				}
   1676 				xmessage[200-1] = '\0';
   1677 				action = xmessage;
   1678 			}
   1679 		}
   1680 	}
   1681 
   1682 	if (cmd) {
   1683 		int rc;
   1684 
   1685 		rfbLog("accept_client: using cmd for: %s\n", addr);
   1686 		rc = run_user_command(cmd, client, "accept", NULL, 0, NULL);
   1687 
   1688 		if (action) {
   1689 			int result;
   1690 
   1691 			if (rc < 0) {
   1692 				rfbLog("accept_client: cannot use negative "
   1693 				    "rc: %d, action %s\n", rc, action);
   1694 				result = 2;
   1695 			} else {
   1696 				result = action_match(action, rc);
   1697 			}
   1698 
   1699 			if (result == 1) {
   1700 				rc = 0;
   1701 			} else if (result == 2) {
   1702 				rc = 1;
   1703 			} else if (result == 3) {
   1704 				rc = 0;
   1705 				rfbLog("accept_client: viewonly: %s\n", addr);
   1706 				client->viewOnly = TRUE;
   1707 			} else {
   1708 				rc = 1;	/* NOTREACHED */
   1709 			}
   1710 		}
   1711 
   1712 		if (rc == 0) {
   1713 			rfbLog("accept_client: accepted: %s\n", addr);
   1714 			return 1;
   1715 		} else {
   1716 			rfbLog("accept_client: rejected: %s\n", addr);
   1717 			return 0;
   1718 		}
   1719 	} else {
   1720 		rfbLog("accept_client: no command, rejecting %s\n", addr);
   1721 		return 0;
   1722 	}
   1723 
   1724 	/* return 0; NOTREACHED */
   1725 }
   1726 
   1727 void check_ipv6_listen(long usec) {
   1728 #if X11VNC_IPV6
   1729 	fd_set fds;
   1730 	struct timeval tv;
   1731 	int nfds, csock = -1, one = 1;
   1732 	struct sockaddr_in6 addr;
   1733 	socklen_t addrlen = sizeof(addr);
   1734 	rfbClientPtr cl;
   1735 	int nmax = 0;
   1736 	char *name;
   1737 
   1738 	if (!ipv6_listen || noipv6) {
   1739 		return;
   1740 	}
   1741 	if (ipv6_listen_fd < 0 && ipv6_http_fd < 0) {
   1742 		return;
   1743 	}
   1744 
   1745 	FD_ZERO(&fds);
   1746 	if (ipv6_listen_fd >= 0) {
   1747 		FD_SET(ipv6_listen_fd, &fds);
   1748 		nmax = ipv6_listen_fd;
   1749 	}
   1750 	if (ipv6_http_fd >= 0 && screen->httpSock < 0) {
   1751 		FD_SET(ipv6_http_fd, &fds);
   1752 		if (ipv6_http_fd > nmax) {
   1753 			nmax = ipv6_http_fd;
   1754 		}
   1755 	}
   1756 
   1757 	tv.tv_sec = 0;
   1758 	tv.tv_usec = 0;
   1759 
   1760 	nfds = select(nmax+1, &fds, NULL, NULL, &tv);
   1761 
   1762 	if (nfds <= 0) {
   1763 		return;
   1764 	}
   1765 
   1766 	if (ipv6_listen_fd >= 0 && FD_ISSET(ipv6_listen_fd, &fds)) {
   1767 
   1768 		csock = accept(ipv6_listen_fd, (struct sockaddr *)&addr, &addrlen);
   1769 		if (csock < 0) {
   1770 			rfbLogPerror("check_ipv6_listen: accept");
   1771 			goto err1;
   1772 		}
   1773 		if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
   1774 			rfbLogPerror("check_ipv6_listen: fcntl");
   1775 			close(csock);
   1776 			goto err1;
   1777 		}
   1778 		if (setsockopt(csock, IPPROTO_TCP, TCP_NODELAY,
   1779 		    (char *)&one, sizeof(one)) < 0) {
   1780 			rfbLogPerror("check_ipv6_listen: setsockopt");
   1781 			close(csock);
   1782 			goto err1;
   1783 		}
   1784 
   1785 		name = ipv6_getipaddr((struct sockaddr *) &addr, addrlen);
   1786 
   1787 		ipv6_client_ip_str = name;
   1788 		cl = rfbNewClient(screen, csock);
   1789 		ipv6_client_ip_str = NULL;
   1790 		if (cl == NULL) {
   1791 			close(csock);
   1792 			goto err1;
   1793 		}
   1794 
   1795 		if (name) {
   1796 			if (cl->host) {
   1797 				free(cl->host);
   1798 			}
   1799 			cl->host = name;
   1800 			rfbLog("ipv6 client: %s\n", name);
   1801 		}
   1802 	}
   1803 
   1804 	err1:
   1805 
   1806 	if (ipv6_http_fd >= 0 && FD_ISSET(ipv6_http_fd, &fds)) {
   1807 
   1808 		csock = accept(ipv6_http_fd, (struct sockaddr *)&addr, &addrlen);
   1809 		if (csock < 0) {
   1810 			rfbLogPerror("check_ipv6_listen: accept");
   1811 			return;
   1812 		}
   1813 		if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
   1814 			rfbLogPerror("check_ipv6_listen: fcntl");
   1815 			close(csock);
   1816 			return;
   1817 		}
   1818 		if (setsockopt(csock, IPPROTO_TCP, TCP_NODELAY,
   1819 		    (char *)&one, sizeof(one)) < 0) {
   1820 			rfbLogPerror("check_ipv6_listen: setsockopt");
   1821 			close(csock);
   1822 			return;
   1823 		}
   1824 
   1825 		rfbLog("check_ipv6_listen: setting httpSock to %d\n", csock);
   1826 		screen->httpSock = csock;
   1827 
   1828 		if (screen->httpListenSock < 0) {
   1829 			/* this may not always work... */
   1830 			int save = screen->httpListenSock;
   1831 			screen->httpListenSock = ipv6_http_fd;
   1832 			rfbLog("check_ipv6_listen: no httpListenSock, calling rfbHttpCheckFds()\n");
   1833 			rfbHttpCheckFds(screen);
   1834 			screen->httpListenSock = save;
   1835 		}
   1836 	}
   1837 #endif
   1838 	if (usec) {}
   1839 }
   1840 
   1841 void check_unix_sock(long usec) {
   1842 	fd_set fds;
   1843 	struct timeval tv;
   1844 	int nfds, csock = -1;
   1845 	rfbClientPtr cl;
   1846 	int nmax = 0;
   1847 	char *name;
   1848 
   1849 	if (!unix_sock || unix_sock_fd < 0) {
   1850 		return;
   1851 	}
   1852 
   1853 	FD_ZERO(&fds);
   1854 	if (unix_sock_fd >= 0) {
   1855 		FD_SET(unix_sock_fd, &fds);
   1856 		nmax = unix_sock_fd;
   1857 	}
   1858 
   1859 	tv.tv_sec = 0;
   1860 	tv.tv_usec = 0;
   1861 
   1862 	nfds = select(nmax+1, &fds, NULL, NULL, &tv);
   1863 
   1864 	if (nfds <= 0) {
   1865 		return;
   1866 	}
   1867 
   1868 	if (unix_sock_fd >= 0 && FD_ISSET(unix_sock_fd, &fds)) {
   1869 		csock = accept_unix(unix_sock_fd);
   1870 		if (csock < 0) {
   1871 			return;
   1872 		}
   1873 		if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
   1874 			rfbLogPerror("check_unix_sock: fcntl");
   1875 			close(csock);
   1876 			return;
   1877 		}
   1878 
   1879 		/* rfbNewClient() will screw us with setsockopt TCP_NODELAY...
   1880 		   you need to comment out in libvncserver/rfbserver.c:
   1881 			rfbLogPerror("setsockopt failed");
   1882 			close(sock);
   1883 			return NULL;
   1884 		 */
   1885 		cl = rfbNewClient(screen, csock);
   1886 
   1887 		if (cl == NULL) {
   1888 			close(csock);
   1889 			return;
   1890 		}
   1891 
   1892 		name = strdup(unix_sock);
   1893 
   1894 		if (name) {
   1895 			if (cl->host) {
   1896 				free(cl->host);
   1897 			}
   1898 			cl->host = name;
   1899 			rfbLog("unix sock client: %s\n", name);
   1900 		}
   1901 	}
   1902 }
   1903 
   1904 /*
   1905  * For the -connect <file> option: periodically read the file looking for
   1906  * a connect string.  If one is found set client_connect to it.
   1907  */
   1908 static void check_connect_file(char *file) {
   1909 	FILE *in;
   1910 	char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX];
   1911 	static int first_warn = 1, truncate_ok = 1;
   1912 	static double last_time = 0.0, delay = 0.5;
   1913 	double now = dnow();
   1914 	struct stat sbuf;
   1915 
   1916 	if (last_time == 0.0) {
   1917 		if (!getenv("X11VNC_APPSHARE_ACTIVE")) {
   1918 			/* skip first */
   1919 			last_time = now;
   1920 		} else {
   1921 			delay = 0.25;
   1922 		}
   1923 	}
   1924 	if (now - last_time < delay) {
   1925 		/* check only about once a second */
   1926 		return;
   1927 	}
   1928 	last_time = now;
   1929 
   1930 	if (! truncate_ok) {
   1931 		/* check if permissions changed */
   1932 		if (access(file, W_OK) == 0) {
   1933 			truncate_ok = 1;
   1934 		} else {
   1935 			return;
   1936 		}
   1937 	}
   1938 
   1939 	if (stat(file, &sbuf) == 0) {
   1940 		/* skip empty file directly */
   1941 		if (sbuf.st_size == 0) {
   1942 			return;
   1943 		}
   1944 	}
   1945 
   1946 	in = fopen(file, "r");
   1947 	if (in == NULL) {
   1948 		if (first_warn) {
   1949 			rfbLog("check_connect_file: fopen failure: %s\n", file);
   1950 			rfbLogPerror("fopen");
   1951 			first_warn = 0;
   1952 		}
   1953 		return;
   1954 	}
   1955 
   1956 	if (fgets(line, VNC_CONNECT_MAX, in) != NULL) {
   1957 		if (sscanf(line, "%s", host) == 1) {
   1958 			if (strlen(host) > 0) {
   1959 				char *str = strdup(host);
   1960 				if (strlen(str) > 38) {
   1961 					char trim[100];
   1962 					trim[0] = '\0';
   1963 					strncat(trim, str, 38);
   1964 					rfbLog("read connect file: %s ...\n",
   1965 					    trim);
   1966 				} else {
   1967 					rfbLog("read connect file: %s\n", str);
   1968 				}
   1969 				if (!strcmp(str, "cmd=stop") &&
   1970 				    dnowx() < 3.0) {
   1971 					rfbLog("ignoring stale cmd=stop\n");
   1972 				} else {
   1973 					client_connect = str;
   1974 				}
   1975 			}
   1976 		}
   1977 	}
   1978 	fclose(in);
   1979 
   1980 	/* truncate file */
   1981 	in = fopen(file, "w");
   1982 	if (in != NULL) {
   1983 		fclose(in);
   1984 	} else {
   1985 		/* disable if we cannot truncate */
   1986 		rfbLog("check_connect_file: could not truncate %s, "
   1987 		   "disabling checking.\n", file);
   1988 		truncate_ok = 0;
   1989 	}
   1990 }
   1991 
   1992 static int socks5_proxy(char *host, int port, int sock) {
   1993 	unsigned char buf[512], tmp[2];
   1994 	char reply[512];
   1995 	int len, n, i, j = 0;
   1996 
   1997 	memset(buf, 0, 512);
   1998 	memset(reply, 0, 512);
   1999 
   2000 	buf[0] = 0x5;
   2001 	buf[1] = 0x1;
   2002 	buf[2] = 0x0;
   2003 
   2004 	write(sock, buf, 3);
   2005 
   2006 	n = read(sock, buf, 2);
   2007 
   2008 	if (n != 2) {
   2009 		rfbLog("socks5_proxy: read error: %d\n", n);
   2010 		close(sock);
   2011 		return 0;
   2012 	}
   2013 	if (buf[0] != 0x5 || buf[1] != 0x0) {
   2014 		rfbLog("socks5_proxy: handshake error: %d %d\n", (int) buf[0], (int) buf[1]);
   2015 		close(sock);
   2016 		return 0;
   2017 	}
   2018 
   2019 	buf[0] = 0x5;
   2020 	buf[1] = 0x1;
   2021 	buf[2] = 0x0;
   2022 	buf[3] = 0x3;
   2023 
   2024 	buf[4] = (unsigned char) strlen(host);
   2025 	strcat((char *) buf+5, host);
   2026 
   2027 	len = 5 + strlen(host);
   2028 
   2029 	buf[len]   = (unsigned char) (port >> 8);
   2030 	buf[len+1] = (unsigned char) (port & 0xff);
   2031 
   2032 	write(sock, buf, len+2);
   2033 
   2034 	for (i=0; i<4; i++) {
   2035 		int n;
   2036 		n = read(sock, tmp, 1);
   2037 		j++;
   2038 		if (n < 0) {
   2039 			if (errno != EINTR) {
   2040 				break;
   2041 			} else {
   2042 				i--;
   2043 				if (j > 100) {
   2044 					break;
   2045 				}
   2046 				continue;
   2047 			}
   2048 		}
   2049 		if (n == 0) {
   2050 			break;
   2051 		}
   2052 		reply[i] = tmp[0];
   2053 	}
   2054 	if (reply[3] == 0x1) {
   2055 		read(sock, reply+4, 4 + 2);
   2056 	} else if (reply[3] == 0x3) {
   2057 		n = read(sock, tmp, 1);
   2058 		reply[4] = tmp[0];
   2059 		read(sock, reply+5, (int) reply[4] + 2);
   2060 	} else if (reply[3] == 0x4) {
   2061 		read(sock, reply+4, 16 + 2);
   2062 	}
   2063 
   2064 	if (0) {
   2065 		int i;
   2066 		for (i=0; i<len+2; i++) {
   2067 			fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
   2068 		}
   2069 		for (i=0; i<len+2; i++) {
   2070 			fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
   2071 		}
   2072 	}
   2073 	if (reply[0] == 0x5 && reply[1] == 0x0 && reply[2] == 0x0) {
   2074 		rfbLog("SOCKS5 connect OK to %s:%d sock=%d\n", host, port, sock);
   2075 		return 1;
   2076 	} else {
   2077 		rfbLog("SOCKS5 error to %s:%d sock=%d\n", host, port, sock);
   2078 		close(sock);
   2079 		return 0;
   2080 	}
   2081 }
   2082 
   2083 static int socks_proxy(char *host, int port, int sock) {
   2084 	unsigned char buf[512], tmp[2];
   2085 	char reply[16];
   2086 	int socks4a = 0, len, i, j = 0, d1, d2, d3, d4;
   2087 
   2088 	memset(buf, 0, 512);
   2089 
   2090 	buf[0] = 0x4;
   2091 	buf[1] = 0x1;
   2092 	buf[2] = (unsigned char) (port >> 8);
   2093 	buf[3] = (unsigned char) (port & 0xff);
   2094 
   2095 
   2096 	if (strlen(host) > 256)  {
   2097 		rfbLog("socks_proxy: hostname too long: %s\n", host);
   2098 		close(sock);
   2099 		return 0;
   2100 	}
   2101 
   2102 	if (!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
   2103 		buf[4] = 127;
   2104 		buf[5] = 0;
   2105 		buf[6] = 0;
   2106 		buf[7] = 1;
   2107 	} else if (sscanf(host, "%d.%d.%d.%d", &d1, &d2, &d3, &d4) == 4) {
   2108 		buf[4] = (unsigned char) d1;
   2109 		buf[5] = (unsigned char) d2;
   2110 		buf[6] = (unsigned char) d3;
   2111 		buf[7] = (unsigned char) d4;
   2112 	} else {
   2113 		buf[4] = 0x0;
   2114 		buf[5] = 0x0;
   2115 		buf[6] = 0x0;
   2116 		buf[7] = 0x3;
   2117 		socks4a = 1;
   2118 	}
   2119 	len = 8;
   2120 
   2121 	strcat((char *)buf+8, "nobody");
   2122 	len += strlen("nobody") + 1;
   2123 
   2124 	if (socks4a) {
   2125 		strcat((char *) buf+8+strlen("nobody") + 1, host);
   2126 		len += strlen(host) + 1;
   2127 	}
   2128 
   2129 	write(sock, buf, len);
   2130 
   2131 	for (i=0; i<8; i++) {
   2132 		int n;
   2133 		n = read(sock, tmp, 1);
   2134 		j++;
   2135 		if (n < 0) {
   2136 			if (errno != EINTR) {
   2137 				break;
   2138 			} else {
   2139 				i--;
   2140 				if (j > 100) {
   2141 					break;
   2142 				}
   2143 				continue;
   2144 			}
   2145 		}
   2146 		if (n == 0) {
   2147 			break;
   2148 		}
   2149 		reply[i] = tmp[0];
   2150 	}
   2151 	if (0) {
   2152 		int i;
   2153 		for (i=0; i<len; i++) {
   2154 			fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
   2155 		}
   2156 		for (i=0; i<8; i++) {
   2157 			fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
   2158 		}
   2159 	}
   2160 	if (reply[0] == 0x0 && reply[1] == 0x5a) {
   2161 		if (socks4a) {
   2162 			rfbLog("SOCKS4a connect OK to %s:%d sock=%d\n", host, port, sock);
   2163 		} else {
   2164 			rfbLog("SOCKS4  connect OK to %s:%d sock=%d\n", host, port, sock);
   2165 		}
   2166 		return 1;
   2167 	} else {
   2168 		if (socks4a) {
   2169 			rfbLog("SOCKS4a error to %s:%d sock=%d\n", host, port, sock);
   2170 		} else {
   2171 			rfbLog("SOCKS4  error to %s:%d sock=%d\n", host, port, sock);
   2172 		}
   2173 		close(sock);
   2174 		return 0;
   2175 	}
   2176 }
   2177 
   2178 #define PXY_HTTP	1
   2179 #define PXY_GET		2
   2180 #define PXY_SOCKS	3
   2181 #define PXY_SOCKS5	4
   2182 #define PXY_SSH		5
   2183 #define PXY 3
   2184 
   2185 static int pxy_get_sock;
   2186 
   2187 static int pconnect(int psock, char *host, int port, int type, char *http_path, char *gethost, int getport) {
   2188 	char reply[4096];
   2189 	int i, ok, len;
   2190 	char *req;
   2191 
   2192 	pxy_get_sock = -1;
   2193 
   2194 	if (type == PXY_SOCKS) {
   2195 		return socks_proxy(host, port, psock);
   2196 	}
   2197 	if (type == PXY_SOCKS5) {
   2198 		return socks5_proxy(host, port, psock);
   2199 	}
   2200 	if (type == PXY_SSH) {
   2201 		return 1;
   2202 	}
   2203 
   2204 	len = strlen("CONNECT ") + strlen(host);
   2205 	if (type == PXY_GET) {
   2206 		len += strlen(http_path) + strlen(gethost);
   2207 		len += strlen("host=") + 1 + strlen("port=") + 1 + 1;
   2208 	}
   2209 	len += 1 + 20 + strlen("HTTP/1.1\r\n") + 1;
   2210 
   2211 	req = (char *)malloc(len);
   2212 
   2213 	if (type == PXY_GET) {
   2214 		int noquery = 0;
   2215 		char *t = strstr(http_path, "__END__");
   2216 		if (t) {
   2217 			noquery = 1;
   2218 			*t = '\0';
   2219 		}
   2220 
   2221 		if (noquery) {
   2222 			sprintf(req, "GET %s HTTP/1.1\r\n", http_path);
   2223 		} else {
   2224 			sprintf(req, "GET %shost=%s&port=%d HTTP/1.1\r\n", http_path, host, port);
   2225 		}
   2226 	} else {
   2227 		sprintf(req, "CONNECT %s:%d HTTP/1.1\r\n", host, port);
   2228 	}
   2229 	rfbLog("http proxy: %s", req);
   2230 	write(psock, req, strlen(req));
   2231 
   2232 	if (type == PXY_GET) {
   2233 		char *t = "Connection: close\r\n";
   2234 		write(psock, t, strlen(t));
   2235 	}
   2236 
   2237 	if (type == PXY_GET) {
   2238 		sprintf(req, "Host: %s:%d\r\n", gethost, getport);
   2239 		rfbLog("http proxy: %s", req);
   2240 		sprintf(req, "Host: %s:%d\r\n\r\n", gethost, getport);
   2241 	} else {
   2242 		sprintf(req, "Host: %s:%d\r\n", host, port);
   2243 		rfbLog("http proxy: %s", req);
   2244 		sprintf(req, "Host: %s:%d\r\n\r\n", host, port);
   2245 	}
   2246 
   2247 	write(psock, req, strlen(req));
   2248 
   2249 	ok = 0;
   2250 	reply[0] = '\0';
   2251 
   2252 	for (i=0; i<4096; i++) {
   2253 		int n;
   2254 		req[0] = req[1] = '\0';
   2255 		n = read(psock, req, 1);
   2256 		if (n < 0) {
   2257 			if (errno != EINTR) {
   2258 				break;
   2259 			} else {
   2260 				continue;
   2261 			}
   2262 		}
   2263 		if (n == 0) {
   2264 			break;
   2265 		}
   2266 		strcat(reply, req);
   2267 		if (strstr(reply, "\r\n\r\n")) {
   2268 			if (strstr(reply, "HTTP/") == reply) {
   2269 				char *q = strchr(reply, ' ');
   2270 				if (q) {
   2271 					q++;
   2272 					if (q[0] == '2' && q[1] == '0' && q[2] == '0' && q[3] == ' ') {
   2273 						ok = 1;
   2274 					}
   2275 				}
   2276 			}
   2277 			break;
   2278 		}
   2279 	}
   2280 
   2281 	if (type == PXY_GET) {
   2282 		char *t1 = strstr(reply, "VNC-IP-Port: ");
   2283 		char *t2 = strstr(reply, "VNC-Host-Port: ");
   2284 		char *s, *newhost = NULL;
   2285 		int newport = 0;
   2286 		fprintf(stderr, "%s\n", reply);
   2287 		if (t1) {
   2288 			t1 += strlen("VNC-IP-Port: ");
   2289 			s = strstr(t1, ":");
   2290 			if (s) {
   2291 				*s = '\0';
   2292 				newhost = strdup(t1);
   2293 				newport = atoi(s+1);
   2294 			}
   2295 		} else if (t2) {
   2296 			t2 += strlen("VNC-Host-Port: ");
   2297 			s = strstr(t2, ":");
   2298 			if (s) {
   2299 				*s = '\0';
   2300 				newhost = strdup(t2);
   2301 				newport = atoi(s+1);
   2302 			}
   2303 		}
   2304 		if (newhost && newport > 0) {
   2305 			rfbLog("proxy GET reconnect to: %s:%d\n", newhost, newport);
   2306 			pxy_get_sock = connect_tcp(newhost, newport);
   2307 		}
   2308 	}
   2309 	free(req);
   2310 
   2311 	return ok;
   2312 }
   2313 
   2314 static int proxy_connect(char *host, int port) {
   2315 	char *p, *q, *str;
   2316 	int i, n, pxy[PXY],pxy_p[PXY];
   2317 	int psock = -1;
   2318 	char *pxy_h[PXY], *pxy_g[PXY];
   2319 
   2320 	if (! connect_proxy) {
   2321 		return -1;
   2322 	}
   2323 	str = strdup(connect_proxy);
   2324 
   2325 	for (i=0; i<PXY; i++) {
   2326 		pxy[i] = 0;
   2327 		pxy_p[i] = 0;
   2328 		pxy_h[i] = NULL;
   2329 		pxy_g[i] = NULL;
   2330 	}
   2331 
   2332 	n = 0;
   2333 	p = str;
   2334 	while (p) {
   2335 		char *hp, *c, *s = NULL;
   2336 
   2337 		q = strchr(p, ',');
   2338 		if (q) {
   2339 			*q = '\0';
   2340 		}
   2341 
   2342 		if (n==0) fprintf(stderr, "\n");
   2343 		rfbLog("proxy_connect[%d]: %s\n", n+1, p);
   2344 
   2345 		pxy[n] = 0;
   2346 		pxy_p[n] = 0;
   2347 		pxy_h[n] = NULL;
   2348 		pxy_g[n] = NULL;
   2349 
   2350 		if (strstr(p, "socks://") == p)	{
   2351 			hp = strstr(p, "://") + 3;
   2352 			pxy[n] = PXY_SOCKS;
   2353 		} else if (strstr(p, "socks4://") == p) {
   2354 			hp = strstr(p, "://") + 3;
   2355 			pxy[n] = PXY_SOCKS;
   2356 		} else if (strstr(p, "socks5://") == p) {
   2357 			hp = strstr(p, "://") + 3;
   2358 			pxy[n] = PXY_SOCKS5;
   2359 		} else if (strstr(p, "ssh://") == p) {
   2360 			if (n != 0) {
   2361 				rfbLog("ssh:// proxy must be the first one\n");
   2362 				clean_up_exit(1);
   2363 			}
   2364 			hp = strstr(p, "://") + 3;
   2365 			pxy[n] = PXY_SSH;
   2366 		} else if (strstr(p, "http://") == p) {
   2367 			hp = strstr(p, "://") + 3;
   2368 			pxy[n] = PXY_HTTP;
   2369 		} else if (strstr(p, "https://") == p) {
   2370 			hp = strstr(p, "://") + 3;
   2371 			pxy[n] = PXY_HTTP;
   2372 		} else {
   2373 			hp = p;
   2374 			pxy[n] = PXY_HTTP;
   2375 		}
   2376 		c = strstr(hp, ":");
   2377 		if (!c && pxy[n] == PXY_SSH) {
   2378 			char *hp2 = (char *) malloc(strlen(hp) + 5);
   2379 			sprintf(hp2, "%s:1", hp);
   2380 			hp = hp2;
   2381 			c = strstr(hp, ":");
   2382 		}
   2383 		if (!c) {
   2384 			pxy[n] = 0;
   2385 			if (q) {
   2386 				*q = ',';
   2387 				p = q + 1;
   2388 			} else {
   2389 				p = NULL;
   2390 			}
   2391 			continue;
   2392 		}
   2393 
   2394 		if (pxy[n] == PXY_HTTP) {
   2395 			s = strstr(c, "/");
   2396 			if (s) {
   2397 				pxy[n] = PXY_GET;
   2398 				pxy_g[n] = strdup(s);
   2399 				*s = '\0';
   2400 			}
   2401 		}
   2402 		pxy_p[n] = atoi(c+1);
   2403 
   2404 		if (pxy_p[n] <= 0) {
   2405 			pxy[n] = 0;
   2406 			pxy_p[n] = 0;
   2407 			if (q) {
   2408 				*q = ',';
   2409 				p = q + 1;
   2410 			} else {
   2411 				p = NULL;
   2412 			}
   2413 			continue;
   2414 		}
   2415 		*c = '\0';
   2416 		pxy_h[n] = strdup(hp);
   2417 
   2418 		if (++n >= PXY) {
   2419 			break;
   2420 		}
   2421 
   2422 		if (q) {
   2423 			*q = ',';
   2424 			p = q + 1;
   2425 		} else {
   2426 			p = NULL;
   2427 		}
   2428 	}
   2429 	free(str);
   2430 
   2431 	if (!n) {
   2432 		psock = -1;
   2433 		goto pxy_clean;
   2434 	}
   2435 
   2436 	if (pxy[0] == PXY_SSH) {
   2437 		int rc, len = 0;
   2438 		char *cmd, *ssh;
   2439 		int sport = find_free_port(7300, 8000);
   2440 		if (getenv("SSH")) {
   2441 			ssh = getenv("SSH");
   2442 		} else {
   2443 			ssh = "ssh";
   2444 		}
   2445 		len = 200 + strlen(ssh) + strlen(pxy_h[0]) + strlen(host);
   2446 		cmd = (char *) malloc(len);
   2447 		if (n == 1) {
   2448 			if (pxy_p[0] <= 1) {
   2449 				sprintf(cmd, "%s -f       -L '%d:%s:%d' '%s' 'sleep 20'", ssh,           sport, host, port, pxy_h[0]);
   2450 			} else {
   2451 				sprintf(cmd, "%s -f -p %d -L '%d:%s:%d' '%s' 'sleep 20'", ssh, pxy_p[0], sport, host, port, pxy_h[0]);
   2452 			}
   2453 		} else {
   2454 			if (pxy_p[0] <= 1) {
   2455 				sprintf(cmd, "%s -f       -L '%d:%s:%d' '%s' 'sleep 20'", ssh,           sport, pxy_h[1], pxy_p[1], pxy_h[0]);
   2456 			} else {
   2457 				sprintf(cmd, "%s -f -p %d -L '%d:%s:%d' '%s' 'sleep 20'", ssh, pxy_p[0], sport, pxy_h[1], pxy_p[1], pxy_h[0]);
   2458 			}
   2459 		}
   2460 		if (no_external_cmds || !cmd_ok("ssh")) {
   2461 			rfbLogEnable(1);
   2462 			rfbLog("cannot run external commands in -nocmds mode:\n");
   2463 			rfbLog("   \"%s\"\n", cmd);
   2464 			rfbLog("   exiting.\n");
   2465 			clean_up_exit(1);
   2466 		}
   2467 		close_exec_fds();
   2468 		fprintf(stderr, "\n");
   2469 		rfbLog("running: %s\n", cmd);
   2470 		rc = system(cmd);
   2471 		free(cmd);
   2472 		if (rc != 0) {
   2473 			psock = -1;
   2474 			goto pxy_clean;
   2475 		}
   2476 		psock = connect_tcp("localhost", sport);
   2477 
   2478 	} else {
   2479 		psock = connect_tcp(pxy_h[0], pxy_p[0]);
   2480 	}
   2481 
   2482 	if (psock < 0) {
   2483 		psock = -1;
   2484 		goto pxy_clean;
   2485 	}
   2486 	rfbLog("opened socket to proxy: %s:%d\n", pxy_h[0], pxy_p[0]);
   2487 
   2488 	if (n >= 2) {
   2489 		if (! pconnect(psock, pxy_h[1], pxy_p[1], pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
   2490 			close(psock); psock = -1; goto pxy_clean;
   2491 		}
   2492 		if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
   2493 
   2494 		if (n >= 3) {
   2495 			if (! pconnect(psock, pxy_h[2], pxy_p[2], pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
   2496 				close(psock); psock = -1; goto pxy_clean;
   2497 			}
   2498 			if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
   2499 			if (! pconnect(psock, host, port, pxy[2], pxy_g[2], pxy_h[2], pxy_p[2])) {
   2500 				close(psock); psock = -1; goto pxy_clean;
   2501 			}
   2502 			if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
   2503 
   2504 		} else {
   2505 			if (! pconnect(psock, host, port, pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
   2506 				close(psock); psock = -1; goto pxy_clean;
   2507 			}
   2508 			if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
   2509 		}
   2510 	} else {
   2511 		if (! pconnect(psock, host, port, pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
   2512 			close(psock); psock = -1; goto pxy_clean;
   2513 		}
   2514 		if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
   2515 	}
   2516 
   2517 	pxy_clean:
   2518 	for (i=0; i < PXY; i++) {
   2519 		if (pxy_h[i] != NULL) {
   2520 			free(pxy_h[i]);
   2521 		}
   2522 		if (pxy_g[i] != NULL) {
   2523 			free(pxy_g[i]);
   2524 		}
   2525 	}
   2526 
   2527 	return psock;
   2528 }
   2529 
   2530 char *get_repeater_string(char *str, int *len) {
   2531 	int pren, which = 0;
   2532 	int prestring_len = 0;
   2533 	char *prestring = NULL, *ptmp = NULL;
   2534 	char *equals = strchr(str, '=');
   2535 	char *plus   = strrchr(str, '+');
   2536 
   2537 	*len = 0;
   2538 	if (!plus || !equals) {
   2539 		return NULL;
   2540 	}
   2541 
   2542 	*plus = '\0';
   2543 	if (strstr(str, "repeater=") == str) {
   2544 		/* ultravnc repeater http://www.uvnc.com/addons/repeater.html */
   2545 		prestring_len = 250;
   2546 		ptmp = (char *) calloc(prestring_len+1, 1);
   2547 		snprintf(ptmp, 250, "%s", str + strlen("repeater="));
   2548 		which = 1;
   2549 	} else if (strstr(str, "pre=") == str) {
   2550 		prestring_len = strlen(str + strlen("pre="));
   2551 		ptmp = (char *) calloc(prestring_len+1, 1);
   2552 		snprintf(ptmp, prestring_len+1, "%s", str + strlen("pre="));
   2553 		which = 2;
   2554 	} else if (sscanf(str, "pre%d=", &pren) == 1) {
   2555 		if (pren > 0 && pren <= 16384) {
   2556 			prestring_len = pren;
   2557 			ptmp = (char *) calloc(prestring_len+1, 1);
   2558 			snprintf(prestring, prestring_len, "%s", equals+1);
   2559 			which = 3;
   2560 		}
   2561 	}
   2562 	if (ptmp != NULL) {
   2563 		int i, k = 0;
   2564 		char *p = ptmp;
   2565 		prestring = (char *)calloc(prestring_len+1, 1);
   2566 		/* translate \n to newline, etc. */
   2567 		for (i=0; i < prestring_len; i++) {
   2568 			if (i < prestring_len-1 && *(p+i) == '\\') {
   2569 				if (*(p+i+1) == 'r') {
   2570 					prestring[k++] = '\r'; i++;
   2571 				} else if (*(p+i+1) == 'n') {
   2572 					prestring[k++] = '\n'; i++;
   2573 				} else if (*(p+i+1) == 't') {
   2574 					prestring[k++] = '\t'; i++;
   2575 				} else if (*(p+i+1) == 'a') {
   2576 					prestring[k++] = '\a'; i++;
   2577 				} else if (*(p+i+1) == 'b') {
   2578 					prestring[k++] = '\b'; i++;
   2579 				} else if (*(p+i+1) == 'v') {
   2580 					prestring[k++] = '\v'; i++;
   2581 				} else if (*(p+i+1) == 'f') {
   2582 					prestring[k++] = '\f'; i++;
   2583 				} else if (*(p+i+1) == '\\') {
   2584 					prestring[k++] = '\\'; i++;
   2585 				} else if (*(p+i+1) == 'c') {
   2586 					prestring[k++] = ','; i++;
   2587 				} else {
   2588 					prestring[k++] = *(p+i);
   2589 				}
   2590 			} else {
   2591 				prestring[k++] = *(p+i);
   2592 			}
   2593 		}
   2594 		if (which == 2) {
   2595 			prestring_len = k;
   2596 		}
   2597 		if (!quiet) {
   2598 			rfbLog("-connect prestring: '%s'\n", prestring);
   2599 		}
   2600 		free(ptmp);
   2601 	}
   2602 	*plus = '+';
   2603 
   2604 	*len = prestring_len;
   2605 	return prestring;
   2606 }
   2607 
   2608 #ifndef USE_TIMEOUT_INTERRUPT
   2609 #define USE_TIMEOUT_INTERRUPT 0
   2610 #endif
   2611 
   2612 static void reverse_connect_timeout (int sig) {
   2613 	rfbLog("sig: %d, reverse_connect_timeout.\n", sig);
   2614 #if USE_TIMEOUT_INTERRUPT
   2615 	rfbLog("reverse_connect_timeout proceeding assuming connect(2) interrupt.\n");
   2616 #else
   2617 	clean_up_exit(0);
   2618 #endif
   2619 }
   2620 
   2621 
   2622 /*
   2623  * Do a reverse connect for a single "host" or "host:port"
   2624  */
   2625 
   2626 static int do_reverse_connect(char *str_in) {
   2627 	rfbClientPtr cl;
   2628 	char *host, *p, *str = str_in, *s = NULL;
   2629 	char *prestring = NULL;
   2630 	int prestring_len = 0;
   2631 	int rport = 5500, len = strlen(str);
   2632 	int set_alarm = 0;
   2633 
   2634 	if (len < 1) {
   2635 		return 0;
   2636 	}
   2637 	if (len > 1024) {
   2638 		rfbLog("reverse_connect: string too long: %d bytes\n", len);
   2639 		return 0;
   2640 	}
   2641 	if (!screen) {
   2642 		rfbLog("reverse_connect: screen not setup yet.\n");
   2643 		return 0;
   2644 	}
   2645 	if (unixpw_in_progress) return 0;
   2646 
   2647 	/* look for repeater pre-string */
   2648 	if (strchr(str, '=') && strrchr(str, '+')
   2649 	    && (strstr(str, "pre") == str || strstr(str, "repeater=") == str)) {
   2650 		prestring = get_repeater_string(str, &prestring_len);
   2651 		str = strrchr(str, '+') + 1;
   2652 	} else if (strrchr(str, '+') && strstr(str, "repeater://") == str) {
   2653 		/* repeater://host:port+string */
   2654 		/*   repeater=string+host:port */
   2655 		char *plus = strrchr(str, '+');
   2656 		str = (char *) malloc(strlen(str_in)+1);
   2657 		s = str;
   2658 		*plus = '\0';
   2659 		sprintf(str, "repeater=%s+%s", plus+1, str_in + strlen("repeater://"));
   2660 		prestring = get_repeater_string(str, &prestring_len);
   2661 		str = strrchr(str, '+') + 1;
   2662 		*plus = '+';
   2663 	}
   2664 
   2665 	/* copy in to host */
   2666 	host = (char *) malloc(len+1);
   2667 	if (! host) {
   2668 		rfbLog("reverse_connect: could not malloc string %d\n", len);
   2669 		return 0;
   2670 	}
   2671 	strncpy(host, str, len);
   2672 	host[len] = '\0';
   2673 
   2674 	/* extract port, if any */
   2675 	if ((p = strrchr(host, ':')) != NULL) {
   2676 		rport = atoi(p+1);
   2677 		if (rport < 0) {
   2678 			rport = -rport;
   2679 		} else if (rport < 20) {
   2680 			rport = 5500 + rport;
   2681 		}
   2682 		*p = '\0';
   2683 	}
   2684 
   2685 	if (ipv6_client_ip_str) {
   2686 		free(ipv6_client_ip_str);
   2687 		ipv6_client_ip_str = NULL;
   2688 	}
   2689 
   2690 	if (use_openssl) {
   2691 		int vncsock;
   2692 		if (connect_proxy) {
   2693 			vncsock = proxy_connect(host, rport);
   2694 		} else {
   2695 			vncsock = connect_tcp(host, rport);
   2696 		}
   2697 		if (vncsock < 0) {
   2698 			rfbLog("reverse_connect: failed to connect to: %s\n", str);
   2699 			return 0;
   2700 		}
   2701 		if (prestring != NULL) {
   2702 			write(vncsock, prestring, prestring_len);
   2703 			free(prestring);
   2704 		}
   2705 /* XXX use header */
   2706 #define OPENSSL_REVERSE 6
   2707 		if (!getenv("X11VNC_DISABLE_SSL_CLIENT_MODE")) {
   2708 			openssl_init(1);
   2709 		}
   2710 
   2711 		if (first_conn_timeout > 0) {
   2712 			set_alarm = 1;
   2713 			signal(SIGALRM, reverse_connect_timeout);
   2714 #if USE_TIMEOUT_INTERRUPT
   2715 			siginterrupt(SIGALRM, 1);
   2716 #endif
   2717 			rfbLog("reverse_connect: using alarm() timeout of %d seconds.\n", first_conn_timeout);
   2718 			alarm(first_conn_timeout);
   2719 		}
   2720 		accept_openssl(OPENSSL_REVERSE, vncsock);
   2721 		if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
   2722 
   2723 		openssl_init(0);
   2724 		free(host);
   2725 		return 1;
   2726 	}
   2727 
   2728 	if (use_stunnel) {
   2729 		if(strcmp(host, "localhost") && strcmp(host, "127.0.0.1")) {
   2730 			if (!getenv("STUNNEL_DISABLE_LOCALHOST")) {
   2731 				rfbLog("reverse_connect: error host not localhost in -stunnel mode.\n");
   2732 				return 0;
   2733 			}
   2734 		}
   2735 	}
   2736 
   2737 	if (unixpw) {
   2738 		int is_localhost = 0, user_disabled_it = 0;
   2739 
   2740 		if(!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
   2741 			is_localhost = 1;
   2742 		}
   2743 		if (getenv("UNIXPW_DISABLE_LOCALHOST")) {
   2744 			user_disabled_it = 1;
   2745 		}
   2746 
   2747 		if (! is_localhost) {
   2748 			if (user_disabled_it) {
   2749 				rfbLog("reverse_connect: warning disabling localhost constraint in -unixpw\n");
   2750 			} else {
   2751 				rfbLog("reverse_connect: error not localhost in -unixpw\n");
   2752 				return 0;
   2753 			}
   2754 		}
   2755 	}
   2756 
   2757 	if (first_conn_timeout > 0) {
   2758 		set_alarm = 1;
   2759 		signal(SIGALRM, reverse_connect_timeout);
   2760 #if USE_TIMEOUT_INTERRUPT
   2761 		siginterrupt(SIGALRM, 1);
   2762 #endif
   2763 		rfbLog("reverse_connect: using alarm() timeout of %d seconds.\n", first_conn_timeout);
   2764 		alarm(first_conn_timeout);
   2765 	}
   2766 
   2767 	if (connect_proxy != NULL) {
   2768 		int sock = proxy_connect(host, rport);
   2769 		if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
   2770 		if (sock >= 0) {
   2771 			if (prestring != NULL) {
   2772 				write(sock, prestring, prestring_len);
   2773 				free(prestring);
   2774 			}
   2775 			cl = create_new_client(sock, 1);
   2776 		} else {
   2777 			return 0;
   2778 		}
   2779 	} else if (prestring != NULL) {
   2780 		int sock = connect_tcp(host, rport);
   2781 		if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
   2782 		if (sock >= 0) {
   2783 			write(sock, prestring, prestring_len);
   2784 			free(prestring);
   2785 			cl = create_new_client(sock, 1);
   2786 		} else {
   2787 			return 0;
   2788 		}
   2789 	} else {
   2790 		cl = rfbReverseConnection(screen, host, rport);
   2791 		if (cl == NULL) {
   2792 			int sock = connect_tcp(host, rport);
   2793 			if (sock >= 0) {
   2794 				cl = create_new_client(sock, 1);
   2795 			}
   2796 		}
   2797 		if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
   2798 		if (cl != NULL && use_threads) {
   2799 			cl->onHold = FALSE;
   2800 			rfbStartOnHoldClient(cl);
   2801 		}
   2802 	}
   2803 
   2804 	free(host);
   2805 
   2806 	if (ipv6_client_ip_str) {
   2807 		free(ipv6_client_ip_str);
   2808 		ipv6_client_ip_str = NULL;
   2809 	}
   2810 
   2811 
   2812 	if (cl == NULL) {
   2813 		if (quiet && connect_or_exit) {
   2814 			rfbLogEnable(1);
   2815 		}
   2816 		rfbLog("reverse_connect: %s failed\n", str);
   2817 		return 0;
   2818 	} else {
   2819 		rfbLog("reverse_connect: %s/%s OK\n", str, cl->host);
   2820 		/* let's see if anyone complains: */
   2821 		if (! getenv("X11VNC_REVERSE_CONNECTION_NO_AUTH")) {
   2822 			rfbLog("reverse_connect: turning on auth for %s\n",
   2823 			    cl->host);
   2824 			cl->reverseConnection = FALSE;
   2825 		}
   2826 		return 1;
   2827 	}
   2828 }
   2829 
   2830 /*
   2831  * Break up comma separated list of hosts and call do_reverse_connect()
   2832  */
   2833 void reverse_connect(char *str) {
   2834 	char *p, *tmp;
   2835 	int sleep_between_host = 300;
   2836 	int sleep_min = 1500, sleep_max = 4500, n_max = 5;
   2837 	int n, tot, t, dt = 100, cnt = 0;
   2838 	int nclients0 = client_count;
   2839 	int lcnt, j;
   2840 	char **list;
   2841 	int do_appshare = 0;
   2842 
   2843 	if (!getenv("X11VNC_REVERSE_USE_OLD_SLEEP")) {
   2844 		sleep_min = 500;
   2845 		sleep_max = 2500;
   2846 	}
   2847 
   2848 	if (unixpw_in_progress) return;
   2849 
   2850 	tmp = strdup(str);
   2851 
   2852 	list = (char **) calloc( (strlen(tmp)+2) * sizeof (char *), 1);
   2853 	lcnt = 0;
   2854 
   2855 	p = strtok(tmp, ", \t\r\n");
   2856 	while (p) {
   2857 		list[lcnt++] = strdup(p);
   2858 		p = strtok(NULL, ", \t\r\n");
   2859 	}
   2860 	free(tmp);
   2861 
   2862 	if (subwin && getenv("X11VNC_APPSHARE_ACTIVE")) {
   2863 		do_appshare = 1;
   2864 		sleep_between_host = 0;	/* too agressive??? */
   2865 	}
   2866 	if (getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST")) {
   2867 		sleep_between_host = atoi(getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST"));
   2868 	}
   2869 
   2870 	if (do_appshare) {
   2871 		if (screen && dpy) {
   2872 			char *s = choose_title(DisplayString(dpy));
   2873 
   2874 			/* mutex */
   2875 			screen->desktopName = s;
   2876 			if (rfb_desktop_name) {
   2877 				free(rfb_desktop_name);
   2878 			}
   2879 			rfb_desktop_name = strdup(s);
   2880 		}
   2881 	}
   2882 
   2883 	for (j = 0; j < lcnt; j++) {
   2884 		p = list[j];
   2885 
   2886 		if ((n = do_reverse_connect(p)) != 0) {
   2887 			int i;
   2888 			progress_client();
   2889 			for (i=0; i < 3; i++) {
   2890 				rfbPE(-1);
   2891 			}
   2892 		}
   2893 		cnt += n;
   2894 		if (list[j+1] != NULL) {
   2895 			t = 0;
   2896 			while (t < sleep_between_host) {
   2897 				double t1, t2;
   2898 				int i;
   2899 				t1 = dnow();
   2900 				for (i=0; i < 8; i++) {
   2901 					rfbPE(-1);
   2902 					if (do_appshare && t == 0) {
   2903 						rfbPE(-1);
   2904 					}
   2905 				}
   2906 				t2 = dnow();
   2907 				t += (int) (1000 * (t2 - t1));
   2908 				if (t >= sleep_between_host) {
   2909 					break;
   2910 				}
   2911 				usleep(dt * 1000);
   2912 				t += dt;
   2913 			}
   2914 		}
   2915 	}
   2916 
   2917 	for (j = 0; j < lcnt; j++) {
   2918 		p = list[j];
   2919 		if (p) free(p);
   2920 	}
   2921 	free(list);
   2922 
   2923 	if (cnt == 0) {
   2924 		if (connect_or_exit) {
   2925 			rfbLogEnable(1);
   2926 			rfbLog("exiting under -connect_or_exit\n");
   2927 			if (gui_pid > 0) {
   2928 				rfbLog("killing gui_pid %d\n", gui_pid);
   2929 				kill(gui_pid, SIGTERM);
   2930 			}
   2931 			clean_up_exit(1);
   2932 		}
   2933 		if (xrandr || xrandr_maybe) {
   2934 			check_xrandr_event("reverse_connect1");
   2935 		}
   2936 		return;
   2937 	}
   2938 
   2939 	/*
   2940 	 * XXX: we need to process some of the initial handshaking
   2941 	 * events, otherwise the client can get messed up (why??)
   2942 	 * so we send rfbProcessEvents() all over the place.
   2943 	 *
   2944 	 * How much is this still needed?
   2945 	 */
   2946 
   2947 	n = cnt;
   2948 	if (n >= n_max) {
   2949 		n = n_max;
   2950 	}
   2951 	t = sleep_max - sleep_min;
   2952 	tot = sleep_min + ((n-1) * t) / (n_max-1);
   2953 
   2954 	if (do_appshare) {
   2955 		tot /= 3;
   2956 		if (tot < dt) {
   2957 			tot = dt;
   2958 		}
   2959 		tot = 0;	/* too agressive??? */
   2960 	}
   2961 
   2962 	if (getenv("X11VNC_REVERSE_SLEEP_MAX")) {
   2963 		tot = atoi(getenv("X11VNC_REVERSE_SLEEP_MAX"));
   2964 	}
   2965 
   2966 	t = 0;
   2967 	while (t < tot) {
   2968 		int i;
   2969 		double t1, t2;
   2970 		t1 = dnow();
   2971 		for (i=0; i < 8; i++) {
   2972 			rfbPE(-1);
   2973 			if (t == 0) rfbPE(-1);
   2974 		}
   2975 		t2 = dnow();
   2976 		t += (int) (1000 * (t2 - t1));
   2977 		if (t >= tot) {
   2978 			break;
   2979 		}
   2980 		usleep(dt * 1000);
   2981 		t += dt;
   2982 	}
   2983 	if (connect_or_exit) {
   2984 		if (client_count <= nclients0)  {
   2985 			for (t = 0; t < 10; t++) {
   2986 				int i;
   2987 				for (i=0; i < 3; i++) {
   2988 					rfbPE(-1);
   2989 				}
   2990 				usleep(100 * 1000);
   2991 			}
   2992 		}
   2993 		if (client_count <= nclients0)  {
   2994 			rfbLogEnable(1);
   2995 			rfbLog("exiting under -connect_or_exit\n");
   2996 			if (gui_pid > 0) {
   2997 				rfbLog("killing gui_pid %d\n", gui_pid);
   2998 				kill(gui_pid, SIGTERM);
   2999 			}
   3000 			clean_up_exit(1);
   3001 		}
   3002 	}
   3003 	if (xrandr || xrandr_maybe) {
   3004 		check_xrandr_event("reverse_connect2");
   3005 	}
   3006 }
   3007 
   3008 /*
   3009  * Routines for monitoring the VNC_CONNECT and X11VNC_REMOTE properties
   3010  * for changes.  The vncconnect(1) will set it on our X display.
   3011  */
   3012 void set_vnc_connect_prop(char *str) {
   3013 	RAWFB_RET_VOID
   3014 #if !NO_X11
   3015 	if (vnc_connect_prop == None) return;
   3016 	XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8,
   3017 	    PropModeReplace, (unsigned char *)str, strlen(str));
   3018 #else
   3019 	if (!str) {}
   3020 #endif	/* NO_X11 */
   3021 }
   3022 
   3023 void set_x11vnc_remote_prop(char *str) {
   3024 	RAWFB_RET_VOID
   3025 #if !NO_X11
   3026 	if (x11vnc_remote_prop == None) return;
   3027 	XChangeProperty(dpy, rootwin, x11vnc_remote_prop, XA_STRING, 8,
   3028 	    PropModeReplace, (unsigned char *)str, strlen(str));
   3029 #else
   3030 	if (!str) {}
   3031 #endif	/* NO_X11 */
   3032 }
   3033 
   3034 void read_vnc_connect_prop(int nomsg) {
   3035 #if NO_X11
   3036 	RAWFB_RET_VOID
   3037 	if (!nomsg) {}
   3038 	return;
   3039 #else
   3040 	Atom type;
   3041 	int format, slen, dlen;
   3042 	unsigned long nitems = 0, bytes_after = 0;
   3043 	unsigned char* data = NULL;
   3044 	int db = 1;
   3045 
   3046 	vnc_connect_str[0] = '\0';
   3047 	slen = 0;
   3048 
   3049 	if (! vnc_connect || vnc_connect_prop == None) {
   3050 		/* not active or problem with VNC_CONNECT atom */
   3051 		return;
   3052 	}
   3053 	RAWFB_RET_VOID
   3054 
   3055 	/* read the property value into vnc_connect_str: */
   3056 	do {
   3057 		if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
   3058 		    vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False,
   3059 		    AnyPropertyType, &type, &format, &nitems, &bytes_after,
   3060 		    &data) == Success) {
   3061 
   3062 			dlen = nitems * (format/8);
   3063 			if (slen + dlen > VNC_CONNECT_MAX) {
   3064 				/* too big */
   3065 				rfbLog("warning: truncating large VNC_CONNECT"
   3066 				   " string > %d bytes.\n", VNC_CONNECT_MAX);
   3067 				XFree_wr(data);
   3068 				break;
   3069 			}
   3070 			memcpy(vnc_connect_str+slen, data, dlen);
   3071 			slen += dlen;
   3072 			vnc_connect_str[slen] = '\0';
   3073 			XFree_wr(data);
   3074 		}
   3075 	} while (bytes_after > 0);
   3076 
   3077 	vnc_connect_str[VNC_CONNECT_MAX] = '\0';
   3078 	if (! db || nomsg) {
   3079 		;
   3080 	} else {
   3081 		rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str);
   3082 	}
   3083 #endif	/* NO_X11 */
   3084 }
   3085 
   3086 void read_x11vnc_remote_prop(int nomsg) {
   3087 #if NO_X11
   3088 	RAWFB_RET_VOID
   3089 	if (!nomsg) {}
   3090 	return;
   3091 #else
   3092 	Atom type;
   3093 	int format, slen, dlen;
   3094 	unsigned long nitems = 0, bytes_after = 0;
   3095 	unsigned char* data = NULL;
   3096 	int db = 1;
   3097 
   3098 	x11vnc_remote_str[0] = '\0';
   3099 	slen = 0;
   3100 
   3101 	if (! vnc_connect || x11vnc_remote_prop == None) {
   3102 		/* not active or problem with X11VNC_REMOTE atom */
   3103 		return;
   3104 	}
   3105 	RAWFB_RET_VOID
   3106 
   3107 	/* read the property value into x11vnc_remote_str: */
   3108 	do {
   3109 		if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
   3110 		    x11vnc_remote_prop, nitems/4, X11VNC_REMOTE_MAX/16, False,
   3111 		    AnyPropertyType, &type, &format, &nitems, &bytes_after,
   3112 		    &data) == Success) {
   3113 
   3114 			dlen = nitems * (format/8);
   3115 			if (slen + dlen > X11VNC_REMOTE_MAX) {
   3116 				/* too big */
   3117 				rfbLog("warning: truncating large X11VNC_REMOTE"
   3118 				   " string > %d bytes.\n", X11VNC_REMOTE_MAX);
   3119 				XFree_wr(data);
   3120 				break;
   3121 			}
   3122 			memcpy(x11vnc_remote_str+slen, data, dlen);
   3123 			slen += dlen;
   3124 			x11vnc_remote_str[slen] = '\0';
   3125 			XFree_wr(data);
   3126 		}
   3127 	} while (bytes_after > 0);
   3128 
   3129 	x11vnc_remote_str[X11VNC_REMOTE_MAX] = '\0';
   3130 	if (! db || nomsg) {
   3131 		;
   3132 	} else if (strstr(x11vnc_remote_str, "ans=stop:N/A,ans=quit:N/A,ans=")) {
   3133 		;
   3134 	} else if (strstr(x11vnc_remote_str, "qry=stop,quit,exit")) {
   3135 		;
   3136 	} else if (strstr(x11vnc_remote_str, "ack=") == x11vnc_remote_str) {
   3137 		;
   3138 	} else if (quiet && strstr(x11vnc_remote_str, "qry=ping") ==
   3139 	    x11vnc_remote_str) {
   3140 		;
   3141 	} else if (strstr(x11vnc_remote_str, "cmd=") &&
   3142 	    strstr(x11vnc_remote_str, "passwd")) {
   3143 		rfbLog("read X11VNC_REMOTE: *\n");
   3144 	} else if (strlen(x11vnc_remote_str) > 36) {
   3145 		char trim[100];
   3146 		trim[0] = '\0';
   3147 		strncat(trim, x11vnc_remote_str, 36);
   3148 		rfbLog("read X11VNC_REMOTE: %s ...\n", trim);
   3149 
   3150 	} else {
   3151 		rfbLog("read X11VNC_REMOTE: %s\n", x11vnc_remote_str);
   3152 	}
   3153 #endif	/* NO_X11 */
   3154 }
   3155 
   3156 extern int rc_npieces;
   3157 
   3158 void grab_state(int *ptr_grabbed, int *kbd_grabbed) {
   3159 	int rcp, rck;
   3160 	double t0, t1;
   3161 	double ta, tb, tc;
   3162 	*ptr_grabbed = -1;
   3163 	*kbd_grabbed = -1;
   3164 
   3165 	if (!dpy) {
   3166 		return;
   3167 	}
   3168 	*ptr_grabbed = 0;
   3169 	*kbd_grabbed = 0;
   3170 
   3171 #if !NO_X11
   3172 	X_LOCK;
   3173 
   3174 	XSync(dpy, False);
   3175 
   3176 	ta = t0 = dnow();
   3177 
   3178 	rcp = XGrabPointer(dpy, window, False, 0, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
   3179 	XUngrabPointer(dpy, CurrentTime);
   3180 
   3181 	tb = dnow();
   3182 
   3183 	rck = XGrabKeyboard(dpy, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
   3184 	XUngrabKeyboard(dpy, CurrentTime);
   3185 
   3186 	tc = dnow();
   3187 
   3188 	XSync(dpy, False);
   3189 
   3190 	t1 = dnow();
   3191 
   3192 	X_UNLOCK;
   3193 	if (rcp == AlreadyGrabbed || rcp == GrabFrozen) {
   3194 		*ptr_grabbed = 1;
   3195 	}
   3196 	if (rck == AlreadyGrabbed || rck == GrabFrozen) {
   3197 		*kbd_grabbed = 1;
   3198 	}
   3199 	if (rc_npieces < 10) {
   3200 		rfbLog("grab_state: checked %d,%d in %.6f sec (%.6f %.6f)\n",
   3201 		    *ptr_grabbed, *kbd_grabbed, t1-t0, tb-ta, tc-tb);
   3202 	}
   3203 #endif
   3204 }
   3205 
   3206 static void pmove(int x, int y) {
   3207 	if (x < 0 || y < 0) {
   3208 		rfbLog("pmove: skipping negative x or y: %d %d\n", x, y);
   3209 		return;
   3210 	}
   3211 	rfbLog("pmove: x y: %d %d\n", x, y);
   3212 	pointer_event(0, x, y, NULL);
   3213 	X_LOCK;
   3214 	XFlush_wr(dpy);
   3215 	X_UNLOCK;
   3216 }
   3217 
   3218 
   3219 char *bcx_xattach(char *str, int *pg_init, int *kg_init) {
   3220 	int grab_check = 1;
   3221 	int shift = 20;
   3222 	int final_x = 30, final_y = 30;
   3223 	int extra_x = -1, extra_y = -1;
   3224 	int t1, t2, dt = 40 * 1000;
   3225 	int ifneeded = 0;
   3226 	char *dir = "none", *flip = "none", *q;
   3227 	int pg1, kg1, pg2, kg2;
   3228 	char _bcx_res[128];
   3229 
   3230 	/* str:[up,down,left,right]+nograbcheck+shift=n+final=x+y+extra_move=x+y+[master_to_slave,slave_to_master,M2S,S2M]+dt=n+retry=n+ifneeded */
   3231 
   3232 	if (strstr(str, "up")) {
   3233 		dir = "up";
   3234 	} else if (strstr(str, "down")) {
   3235 		dir = "down";
   3236 	} else if (strstr(str, "left")) {
   3237 		dir = "left";
   3238 	} else if (strstr(str, "right")) {
   3239 		dir = "right";
   3240 	} else {
   3241 		return strdup("FAIL,NO_DIRECTION_SPECIFIED");
   3242 	}
   3243 
   3244 	if (strstr(str, "master_to_slave") || strstr(str, "M2S")) {
   3245 		flip = "M2S";
   3246 	} else if (strstr(str, "slave_to_master") || strstr(str, "S2M")) {
   3247 		flip = "S2M";
   3248 	} else {
   3249 		return strdup("FAIL,NO_MODE_CHANGE_SPECIFIED");
   3250 	}
   3251 
   3252 	if (strstr(str, "nograbcheck")) {
   3253 		grab_check = 0;
   3254 	}
   3255 	if (strstr(str, "ifneeded")) {
   3256 		ifneeded = 1;
   3257 	}
   3258 	q = strstr(str, "shift=");
   3259 	if (q && sscanf(q, "shift=%d", &t1) == 1) {
   3260 		shift = t1;
   3261 	}
   3262 	q = strstr(str, "final=");
   3263 	if (q && sscanf(q, "final=%d+%d", &t1, &t2) == 2) {
   3264 		final_x = t1;
   3265 		final_y = t2;
   3266 	}
   3267 	q = strstr(str, "extra_move=");
   3268 	if (q && sscanf(q, "extra_move=%d+%d", &t1, &t2) == 2) {
   3269 		extra_x = t1;
   3270 		extra_y = t2;
   3271 	}
   3272 	q = strstr(str, "dt=");
   3273 	if (q && sscanf(q, "dt=%d", &t1) == 1) {
   3274 		dt = t1 * 1000;
   3275 	}
   3276 
   3277 	if (grab_check) {
   3278 		int read_init = 0;
   3279 
   3280 		if (*pg_init >=0 && *kg_init >=0)  {
   3281 			pg1 = *pg_init;
   3282 			kg1 = *kg_init;
   3283 			read_init = 1;
   3284 		} else {
   3285 			grab_state(&pg1, &kg1);
   3286 			read_init = 0;
   3287 		}
   3288 
   3289 		if (!strcmp(flip, "M2S")) {
   3290 			if (ifneeded && pg1 == 1 && kg1 == 1) {
   3291 				rfbLog("bcx_xattach: M2S grab state is already what we want, skipping moves:  %d,%d\n", pg1, kg1);
   3292 				return strdup("DONE,GRAB_OK");
   3293 			}
   3294 		} else if (!strcmp(flip, "S2M")) {
   3295 			if (ifneeded && pg1 == 0 && kg1 == 0) {
   3296 				rfbLog("bcx_xattach: S2M grab state is already what we want, skipping moves:  %d,%d\n", pg1, kg1);
   3297 				return strdup("DONE,GRAB_OK");
   3298 			}
   3299 		}
   3300 
   3301 		if (read_init) {
   3302 			;
   3303 		} else if (!strcmp(flip, "M2S")) {
   3304 			if (pg1 != 0 || kg1 != 0) {
   3305 				rfbLog("bcx_xattach: M2S init grab state incorrect:  %d,%d\n", pg1, kg1);
   3306 				usleep(2*dt);
   3307 				grab_state(&pg1, &kg1);
   3308 				rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg1, kg1);
   3309 			}
   3310 		} else if (!strcmp(flip, "S2M")) {
   3311 			if (pg1 != 1 || kg1 != 1) {
   3312 				rfbLog("bcx_xattach: S2M init grab state incorrect:  %d,%d\n", pg1, kg1);
   3313 				usleep(2*dt);
   3314 				grab_state(&pg1, &kg1);
   3315 				rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg1, kg1);
   3316 			}
   3317 		}
   3318 		if (!read_init) {
   3319 			*pg_init = pg1;
   3320 			*kg_init = kg1;
   3321 		}
   3322 	}
   3323 
   3324 	/*
   3325 	 * A guide for BARCO xattach:
   3326 	 *
   3327 	 *   For -cursor_rule 'b(0):%:t(1),t(1):%:b(0)'
   3328 	 *	down+M2S  up+S2M
   3329 	 *   For -cursor_rule 'r(0):%:l(1),l(1):%:r(0)'
   3330 	 *	right+M2S  left+S2M
   3331 	 *
   3332 	 *   For -cursor_rule 't(0):%:b(1),b(1):%:t(0)'
   3333 	 *	up+M2S  down+S2M
   3334 	 *   For -cursor_rule 'l(0):%:r(1),r(1):%:l(0)'
   3335 	 *	left+M2S  right+S2M
   3336 	 *   For -cursor_rule 'l(0):%:r(1),r(1):%:l(0),r(0):%:l(1),l(1):%:r(0)'
   3337 	 *	left+M2S  right+S2M  (we used to do both 'right')
   3338 	 */
   3339 
   3340 	if (!strcmp(flip, "M2S")) {
   3341 		if (!strcmp(dir, "up")) {
   3342 			pmove(shift, 0);		/* go to top edge */
   3343 			usleep(dt);
   3344 			pmove(shift+1, 0);		/* move 1 for MotionNotify */
   3345 		} else if (!strcmp(dir, "down")) {
   3346 			pmove(shift,   dpy_y-1);	/* go to bottom edge */
   3347 			usleep(dt);
   3348 			pmove(shift+1, dpy_y-1);	/* move 1 for MotionNotify */
   3349 		} else if (!strcmp(dir, "left")) {
   3350 			pmove(0, shift);		/* go to left edge */
   3351 			usleep(dt);
   3352 			pmove(0, shift+1);		/* move 1 for MotionNotify */
   3353 		} else if (!strcmp(dir, "right")) {
   3354 			pmove(dpy_x-1, shift);		/* go to right edge */
   3355 			usleep(dt);
   3356 			pmove(dpy_x-1, shift+1);	/* move 1 for Motion Notify  */
   3357 		}
   3358 	} else if (!strcmp(flip, "S2M")) {
   3359 		int dts = dt/2;
   3360 		if (!strcmp(dir, "up")) {
   3361 			pmove(shift, 2);		/* Approach top edge in 3 moves.  1st move */
   3362 			usleep(dts);
   3363 			pmove(shift, 1);		/* 2nd move */
   3364 			usleep(dts);
   3365 			pmove(shift, 0);		/* 3rd move */
   3366 			usleep(dts);
   3367 			pmove(shift+1, 0);		/* move 1 for MotionNotify */
   3368 			usleep(dts);
   3369 			pmove(shift+1, dpy_y-2);	/* go to height-2 for extra pixel (slave y now == 0?) */
   3370 			usleep(dts);
   3371 			pmove(shift,   dpy_y-2);	/* move 1 for MotionNotify */
   3372 			usleep(dts);
   3373 			pmove(shift, 1);		/* go to 1 to be sure slave y == 0 */
   3374 			usleep(dts);
   3375 			pmove(shift+1, 1);		/* move 1 for MotionNotify */
   3376 		} else if (!strcmp(dir, "down")) {
   3377 			pmove(shift,   dpy_y-3);	/* Approach bottom edge in 3 moves.  1st move */
   3378 			usleep(dts);
   3379 			pmove(shift,   dpy_y-2);	/* 2nd move */
   3380 			usleep(dts);
   3381 			pmove(shift,   dpy_y-1);	/* 3rd move */
   3382 			usleep(dts);
   3383 			pmove(shift+1, dpy_y-1);	/* move 1 for MotionNotify */
   3384 			usleep(dts);
   3385 			pmove(shift+1, 1);		/* go to 1 for extra pixel (slave y now == dpy_y-1?) */
   3386 			usleep(dts);
   3387 			pmove(shift, 1);		/* move 1 for MotionNotify */
   3388 			usleep(dts);
   3389 			pmove(shift,   dpy_y-2);	/* go to dpy_y-2 to be sure slave y == dpy_y-1 */
   3390 			usleep(dts);
   3391 			pmove(shift+1, dpy_y-2);	/* move 1 for MotionNotify */
   3392 		} else if (!strcmp(dir, "left")) {
   3393 			pmove(2, shift);		/* Approach left edge in 3 moves.  1st move */
   3394 			usleep(dts);
   3395 			pmove(1, shift);		/* 2nd move */
   3396 			usleep(dts);
   3397 			pmove(0, shift);		/* 3rd move */
   3398 			usleep(dts);
   3399 			pmove(0, shift+1);		/* move 1 for MotionNotify */
   3400 			usleep(dts);
   3401 			pmove(dpy_x-2, shift+1);	/* go to width-2 for extra pixel (slave x now == 0?) */
   3402 			usleep(dts);
   3403 			pmove(dpy_x-2, shift);		/* move 1 for MotionNotify */
   3404 			usleep(dts);
   3405 			pmove(1, shift);		/* go to 1 to be sure slave x == 0 */
   3406 			usleep(dts);
   3407 			pmove(1, shift+1);		/* move 1 for MotionNotify */
   3408 		} else if (!strcmp(dir, "right")) {
   3409 			pmove(dpy_x-3, shift);		/* Approach right edge in 3 moves.  1st move */
   3410 			usleep(dts);
   3411 			pmove(dpy_x-2, shift);		/* 2nd move */
   3412 			usleep(dts);
   3413 			pmove(dpy_x-1, shift);		/* 3rd move */
   3414 			usleep(dts);
   3415 			pmove(dpy_x-1, shift+1);	/* move 1 for MotionNotify */
   3416 			usleep(dts);
   3417 			pmove(1, shift+1);		/* go to 1 to extra pixel (slave x now == dpy_x-1?) */
   3418 			usleep(dts);
   3419 			pmove(1, shift);		/* move 1 for MotionNotify */
   3420 			usleep(dts);
   3421 			pmove(dpy_x-2, shift);		/* go to dpy_x-2 to be sure slave x == dpy_x-1 */
   3422 			usleep(dts);
   3423 			pmove(dpy_x-2, shift+1);	/* move 1 for MotionNotify */
   3424 		}
   3425 	}
   3426 
   3427 	usleep(dt);
   3428 	pmove(final_x, final_y);
   3429 	usleep(dt);
   3430 
   3431 	if (extra_x >= 0 && extra_y >= 0) {
   3432 		pmove(extra_x, extra_y);
   3433 		usleep(dt);
   3434 	}
   3435 
   3436 	strcpy(_bcx_res, "DONE");
   3437 
   3438 	if (grab_check) {
   3439 		char st[64];
   3440 
   3441 		usleep(3*dt);
   3442 		grab_state(&pg2, &kg2);
   3443 
   3444 		if (!strcmp(flip, "M2S")) {
   3445 			if (pg2 != 1 || kg2 != 1) {
   3446 				rfbLog("bcx_xattach: M2S fini grab state incorrect:  %d,%d\n", pg2, kg2);
   3447 				usleep(2*dt);
   3448 				grab_state(&pg2, &kg2);
   3449 				rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg2, kg2);
   3450 			}
   3451 		} else if (!strcmp(flip, "S2M")) {
   3452 			if (pg2 != 0 || kg2 != 0) {
   3453 				rfbLog("bcx_xattach: S2M fini grab state incorrect:  %d,%d\n", pg2, kg2);
   3454 				usleep(2*dt);
   3455 				grab_state(&pg2, &kg2);
   3456 				rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg2, kg2);
   3457 			}
   3458 		}
   3459 
   3460 		sprintf(st, ":%d,%d-%d,%d", pg1, kg1, pg2, kg2);
   3461 
   3462 		if (getenv("GRAB_CHECK_LOOP")) {
   3463 			int i, n = atoi(getenv("GRAB_CHECK_LOOP"));
   3464 			rfbLog("grab st: %s\n", st);
   3465 			for (i=0; i < n; i++) {
   3466 				usleep(dt);
   3467 				grab_state(&pg2, &kg2);
   3468 				sprintf(st, ":%d,%d-%d,%d", pg1, kg1, pg2, kg2);
   3469 				rfbLog("grab st: %s\n", st);
   3470 			}
   3471 		}
   3472 
   3473 		if (!strcmp(flip, "M2S")) {
   3474 			if (pg1 == 0 && kg1 == 0 && pg2 == 1 && kg2 == 1) {
   3475 				strcat(_bcx_res, ",GRAB_OK");
   3476 			} else {
   3477 				rfbLog("bcx_xattach: M2S grab state incorrect: %d,%d -> %d,%d\n", pg1, kg1, pg2, kg2);
   3478 				strcat(_bcx_res, ",GRAB_FAIL");
   3479 				if (pg2 == 1 && kg2 == 1) {
   3480 					strcat(_bcx_res, "_INIT");
   3481 				} else if (pg1 == 0 && kg1 == 0) {
   3482 					strcat(_bcx_res, "_FINAL");
   3483 				}
   3484 				strcat(_bcx_res, st);
   3485 			}
   3486 		} else if (!strcmp(flip, "S2M")) {
   3487 			if (pg1 == 1 && kg1 == 1 && pg2 == 0 && kg2 == 0) {
   3488 				strcat(_bcx_res, ",GRAB_OK");
   3489 			} else {
   3490 				rfbLog("bcx_xattach: S2M grab state incorrect: %d,%d -> %d,%d\n", pg1, kg1, pg2, kg2);
   3491 				strcat(_bcx_res, ",GRAB_FAIL");
   3492 				if (pg2 == 0 && kg2 == 0) {
   3493 					strcat(_bcx_res, "_INIT");
   3494 				} else if (pg1 == 1 && kg1 == 1) {
   3495 					strcat(_bcx_res, "_FINAL");
   3496 				}
   3497 				strcat(_bcx_res, st);
   3498 			}
   3499 		}
   3500 	}
   3501 	return strdup(_bcx_res);
   3502 }
   3503 
   3504 int set_xprop(char *prop, Window win, char *value) {
   3505 	int rc = -1;
   3506 #if !NO_X11
   3507 	Atom aprop;
   3508 
   3509 	RAWFB_RET(rc)
   3510 
   3511 	if (!prop || !value) {
   3512 		return rc;
   3513 	}
   3514 	if (win == None) {
   3515 		win = rootwin;
   3516 	}
   3517 	aprop = XInternAtom(dpy, prop, False);
   3518 	if (aprop == None) {
   3519 		return rc;
   3520 	}
   3521 	rc = XChangeProperty(dpy, win, aprop, XA_STRING, 8,
   3522 	    PropModeReplace, (unsigned char *)value, strlen(value));
   3523 	return rc;
   3524 #else
   3525 	RAWFB_RET(rc)
   3526 	if (!prop || !win || !value) {}
   3527 	return rc;
   3528 #endif	/* NO_X11 */
   3529 }
   3530 
   3531 char *get_xprop(char *prop, Window win) {
   3532 #if NO_X11
   3533 	RAWFB_RET(NULL)
   3534 	if (!prop || !win) {}
   3535 	return NULL;
   3536 #else
   3537 	Atom type, aprop;
   3538 	int format, slen, dlen;
   3539 	unsigned long nitems = 0, bytes_after = 0;
   3540 	unsigned char* data = NULL;
   3541 	char get_str[VNC_CONNECT_MAX+1];
   3542 
   3543 	RAWFB_RET(NULL)
   3544 
   3545 	if (prop == NULL || !strcmp(prop, "")) {
   3546 		return NULL;
   3547 	}
   3548 	if (win == None) {
   3549 		win = rootwin;
   3550 	}
   3551 	aprop = XInternAtom(dpy, prop, True);
   3552 	if (aprop == None) {
   3553 		return NULL;
   3554 	}
   3555 
   3556 	get_str[0] = '\0';
   3557 	slen = 0;
   3558 
   3559 	/* read the property value into get_str: */
   3560 	do {
   3561 		if (XGetWindowProperty(dpy, win, aprop, nitems/4,
   3562 		    VNC_CONNECT_MAX/16, False, AnyPropertyType, &type,
   3563 		    &format, &nitems, &bytes_after, &data) == Success) {
   3564 
   3565 			dlen = nitems * (format/8);
   3566 			if (slen + dlen > VNC_CONNECT_MAX) {
   3567 				/* too big */
   3568 				rfbLog("get_xprop: warning: truncating large '%s'"
   3569 				   " string > %d bytes.\n", prop, VNC_CONNECT_MAX);
   3570 				XFree_wr(data);
   3571 				break;
   3572 			}
   3573 			memcpy(get_str+slen, data, dlen);
   3574 			slen += dlen;
   3575 			get_str[slen] = '\0';
   3576 			XFree_wr(data);
   3577 		}
   3578 	} while (bytes_after > 0);
   3579 
   3580 	get_str[VNC_CONNECT_MAX] = '\0';
   3581 	rfbLog("get_prop: read: '%s' = '%s'\n", prop, get_str);
   3582 
   3583 	return strdup(get_str);
   3584 #endif	/* NO_X11 */
   3585 }
   3586 
   3587 static char _win_fmt[1000];
   3588 
   3589 static char *win_fmt(Window win, XWindowAttributes a) {
   3590 	memset(_win_fmt, 0, sizeof(_win_fmt));
   3591 	sprintf(_win_fmt, "0x%lx:%dx%dx%d+%d+%d-map:%d-bw:%d-cl:%d-vis:%d-bs:%d/%d",
   3592 	    win, a.width, a.height, a.depth, a.x, a.y, a.map_state, a.border_width, a.class,
   3593 	    (int) ((a.visual)->visualid), a.backing_store, a.save_under);
   3594 	return _win_fmt;
   3595 }
   3596 
   3597 char *wininfo(Window win, int show_children) {
   3598 #if NO_X11
   3599 	RAWFB_RET(NULL)
   3600 	if (!win || !show_children) {}
   3601 	return NULL;
   3602 #else
   3603 	XWindowAttributes attr;
   3604 	int n, size = X11VNC_REMOTE_MAX;
   3605 	char get_str[X11VNC_REMOTE_MAX+1];
   3606 	unsigned int nchildren;
   3607 	Window rr, pr, *children;
   3608 
   3609 	RAWFB_RET(NULL)
   3610 
   3611 	if (win == None) {
   3612 		return strdup("None");
   3613 	}
   3614 
   3615 	X_LOCK;
   3616 	if (!valid_window(win, &attr, 1)) {
   3617 		X_UNLOCK;
   3618 		return strdup("Invalid");
   3619 	}
   3620 	get_str[0] = '\0';
   3621 
   3622 	if (show_children) {
   3623 		XQueryTree_wr(dpy, win, &rr, &pr, &children, &nchildren);
   3624 	} else {
   3625 		nchildren = 1;
   3626 		children = (Window *) calloc(2 * sizeof(Window), 1);
   3627 		children[0] = win;
   3628 	}
   3629 	for (n=0; n < (int) nchildren; n++) {
   3630 		char tmp[32];
   3631 		char *str = "Invalid";
   3632 		Window w = children[n];
   3633 		if (valid_window(w, &attr, 1)) {
   3634 			if (!show_children) {
   3635 				str = win_fmt(w, attr);
   3636 			} else {
   3637 				sprintf(tmp, "0x%lx", w);
   3638 				str = tmp;
   3639 			}
   3640 		}
   3641 		if ((int) (strlen(get_str) + 1 + strlen(str)) >= size) {
   3642 			break;
   3643 		}
   3644 		if (n > 0) {
   3645 			strcat(get_str, ",");
   3646 		}
   3647 		strcat(get_str, str);
   3648 	}
   3649 	get_str[size] = '\0';
   3650 	if (!show_children) {
   3651 		free(children);
   3652 	} else if (nchildren) {
   3653 		XFree_wr(children);
   3654 	}
   3655 	rfbLog("wininfo computed: %s\n", get_str);
   3656 	X_UNLOCK;
   3657 
   3658 	return strdup(get_str);
   3659 #endif	/* NO_X11 */
   3660 }
   3661 
   3662 /*
   3663  * check if client_connect has been set, if so make the reverse connections.
   3664  */
   3665 static void send_client_connect(void) {
   3666 	if (client_connect != NULL) {
   3667 		char *str = client_connect;
   3668 		if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) {
   3669 			process_remote_cmd(client_connect, 0);
   3670 		} else if (strstr(str, "ans=") == str
   3671 		    || strstr(str, "aro=") == str) {
   3672 			;
   3673 		} else if (strstr(str, "ack=") == str) {
   3674 			;
   3675 		} else {
   3676 			reverse_connect(client_connect);
   3677 		}
   3678 		free(client_connect);
   3679 		client_connect = NULL;
   3680 	}
   3681 }
   3682 
   3683 /*
   3684  * monitor the various input methods
   3685  */
   3686 void check_connect_inputs(void) {
   3687 
   3688 	if (unixpw_in_progress) return;
   3689 
   3690 	/* flush any already set: */
   3691 	send_client_connect();
   3692 
   3693 	/* connect file: */
   3694 	if (client_connect_file != NULL) {
   3695 		check_connect_file(client_connect_file);
   3696 	}
   3697 	send_client_connect();
   3698 
   3699 	/* VNC_CONNECT property (vncconnect program) */
   3700 	if (vnc_connect && *vnc_connect_str != '\0') {
   3701 		client_connect = strdup(vnc_connect_str);
   3702 		vnc_connect_str[0] = '\0';
   3703 	}
   3704 	send_client_connect();
   3705 
   3706 	/* X11VNC_REMOTE property */
   3707 	if (vnc_connect && *x11vnc_remote_str != '\0') {
   3708 		client_connect = strdup(x11vnc_remote_str);
   3709 		x11vnc_remote_str[0] = '\0';
   3710 	}
   3711 	send_client_connect();
   3712 }
   3713 
   3714 void check_gui_inputs(void) {
   3715 	int i, gnmax = 0, n = 0, nfds;
   3716 	int socks[ICON_MODE_SOCKS];
   3717 	fd_set fds;
   3718 	struct timeval tv;
   3719 	char buf[X11VNC_REMOTE_MAX+1];
   3720 	ssize_t nbytes;
   3721 
   3722 	if (unixpw_in_progress) return;
   3723 
   3724 	for (i=0; i<ICON_MODE_SOCKS; i++) {
   3725 		if (icon_mode_socks[i] >= 0) {
   3726 			socks[n++] = i;
   3727 			if (icon_mode_socks[i] > gnmax) {
   3728 				gnmax = icon_mode_socks[i];
   3729 			}
   3730 		}
   3731 	}
   3732 
   3733 	if (! n) {
   3734 		return;
   3735 	}
   3736 
   3737 	FD_ZERO(&fds);
   3738 	for (i=0; i<n; i++) {
   3739 		FD_SET(icon_mode_socks[socks[i]], &fds);
   3740 	}
   3741 	tv.tv_sec = 0;
   3742 	tv.tv_usec = 0;
   3743 
   3744 	nfds = select(gnmax+1, &fds, NULL, NULL, &tv);
   3745 	if (nfds <= 0) {
   3746 		return;
   3747 	}
   3748 
   3749 	for (i=0; i<n; i++) {
   3750 		int k, fd = icon_mode_socks[socks[i]];
   3751 		char *p;
   3752 		char **list;
   3753 		int lind;
   3754 
   3755 		if (! FD_ISSET(fd, &fds)) {
   3756 			continue;
   3757 		}
   3758 		for (k=0; k<=X11VNC_REMOTE_MAX; k++) {
   3759 			buf[k] = '\0';
   3760 		}
   3761 		nbytes = read(fd, buf, X11VNC_REMOTE_MAX);
   3762 		if (nbytes <= 0) {
   3763 			close(fd);
   3764 			icon_mode_socks[socks[i]] = -1;
   3765 			continue;
   3766 		}
   3767 
   3768 		list = (char **) calloc((strlen(buf)+2) * sizeof(char *), 1);
   3769 
   3770 		lind = 0;
   3771 		p = strtok(buf, "\r\n");
   3772 		while (p) {
   3773 			list[lind++] = strdup(p);
   3774 			p = strtok(NULL, "\r\n");
   3775 		}
   3776 
   3777 		lind = 0;
   3778 		while (list[lind] != NULL) {
   3779 			p = list[lind++];
   3780 			if (strstr(p, "cmd=") == p ||
   3781 			    strstr(p, "qry=") == p) {
   3782 				char *str = process_remote_cmd(p, 1);
   3783 				if (! str) {
   3784 					str = strdup("");
   3785 				}
   3786 				nbytes = write(fd, str, strlen(str));
   3787 				write(fd, "\n", 1);
   3788 				free(str);
   3789 				if (nbytes < 0) {
   3790 					close(fd);
   3791 					icon_mode_socks[socks[i]] = -1;
   3792 					break;
   3793 				}
   3794 			}
   3795 		}
   3796 
   3797 		lind = 0;
   3798 		while (list[lind] != NULL) {
   3799 			p = list[lind++];
   3800 			if (p) free(p);
   3801 		}
   3802 		free(list);
   3803 	}
   3804 }
   3805 
   3806 rfbClientPtr create_new_client(int sock, int start_thread) {
   3807 	rfbClientPtr cl;
   3808 
   3809 	if (!screen) {
   3810 		return NULL;
   3811 	}
   3812 
   3813 	cl = rfbNewClient(screen, sock);
   3814 
   3815 	if (cl == NULL) {
   3816 		return NULL;
   3817 	}
   3818 	if (use_threads) {
   3819 		cl->onHold = FALSE;
   3820 		if (start_thread) {
   3821 			rfbStartOnHoldClient(cl);
   3822 		}
   3823 	}
   3824 	return cl;
   3825 }
   3826 
   3827 static int turn_off_truecolor = 0;
   3828 
   3829 static void turn_off_truecolor_ad(rfbClientPtr client) {
   3830 	if (client) {}
   3831 	if (turn_off_truecolor) {
   3832 		rfbLog("turning off truecolor advertising.\n");
   3833 		/* mutex */
   3834 		screen->serverFormat.trueColour = FALSE;
   3835 		screen->displayHook = NULL;
   3836 		screen->serverFormat.redShift   = 0;
   3837 		screen->serverFormat.greenShift = 0;
   3838 		screen->serverFormat.blueShift  = 0;
   3839 		screen->serverFormat.redMax     = 0;
   3840 		screen->serverFormat.greenMax   = 0;
   3841 		screen->serverFormat.blueMax    = 0;
   3842 		turn_off_truecolor = 0;
   3843 	}
   3844 }
   3845 
   3846 /*
   3847  * some overrides for the local console text chat.
   3848  * could be useful in general for local helpers.
   3849  */
   3850 
   3851 rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len) {
   3852 	if (response || len) {}
   3853 	if (cl != chat_window_client) {
   3854 		rfbLog("invalid client during chat_helper login\n");
   3855 		return FALSE;
   3856 	} else {
   3857 		if (!cl->host) {
   3858 			rfbLog("empty cl->host during chat_helper login\n");
   3859 			return FALSE;
   3860 		}
   3861 		if (strcmp(cl->host, "127.0.0.1")) {
   3862 			rfbLog("invalid cl->host during chat_helper login: %s\n", cl->host);
   3863 			return FALSE;
   3864 		}
   3865 		rfbLog("chat_helper login accepted\n");
   3866 		return TRUE;
   3867 	}
   3868 }
   3869 
   3870 enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client) {
   3871 	if (client) {}
   3872 	client->clientGoneHook = client_gone_chat_helper;
   3873 	rfbLog("new chat helper\n");
   3874 	return(RFB_CLIENT_ACCEPT);
   3875 }
   3876 
   3877 void client_gone_chat_helper(rfbClientPtr client) {
   3878 	if (client) {}
   3879 	rfbLog("finished chat helper\n");
   3880 	chat_window_client = NULL;
   3881 }
   3882 
   3883 void client_set_net(rfbClientPtr client) {
   3884 	ClientData *cd;
   3885 	if (client == NULL) {
   3886 		return;
   3887 	}
   3888 	cd = (ClientData *) client->clientData;
   3889 	if (cd == NULL) {
   3890 		return;
   3891 	}
   3892 	if (cd->client_port < 0) {
   3893 		double dt = dnow();
   3894 		cd->client_port = get_remote_port(client->sock);
   3895 		cd->server_port = get_local_port(client->sock);
   3896 		cd->server_ip   = get_local_host(client->sock);
   3897 		cd->hostname = ip2host(client->host);
   3898 		rfbLog("client_set_net: %s  %.4f\n", client->host, dnow() - dt);
   3899 	}
   3900 }
   3901 /*
   3902  * libvncserver callback for when a new client connects
   3903  */
   3904 enum rfbNewClientAction new_client(rfbClientPtr client) {
   3905 	ClientData *cd;
   3906 
   3907 	CLIENT_LOCK;
   3908 
   3909 	last_event = last_input = time(NULL);
   3910 
   3911 	latest_client = client;
   3912 
   3913 	if (inetd) {
   3914 		/*
   3915 		 * Set this so we exit as soon as connection closes,
   3916 		 * otherwise client_gone is only called after RFB_CLIENT_ACCEPT
   3917 		 */
   3918 		if (inetd_client == NULL) {
   3919 			inetd_client = client;
   3920 			client->clientGoneHook = client_gone;
   3921 		}
   3922 	}
   3923 
   3924 	clients_served++;
   3925 
   3926 	if (use_openssl || use_stunnel) {
   3927 		if (! ssl_initialized) {
   3928 			rfbLog("denying additional client: %s ssl not setup"
   3929 			    " yet.\n", client->host);
   3930 			CLIENT_UNLOCK;
   3931 			return(RFB_CLIENT_REFUSE);
   3932 		}
   3933 	}
   3934 	if (unixpw_in_progress) {
   3935 		rfbLog("denying additional client: %s during -unixpw login.\n",
   3936 		     client->host);
   3937 		CLIENT_UNLOCK;
   3938 		return(RFB_CLIENT_REFUSE);
   3939 	}
   3940 	if (connect_once) {
   3941 		if (screen->dontDisconnect && screen->neverShared) {
   3942 			if (! shared && accepted_client) {
   3943 				rfbLog("denying additional client: %s:%d\n",
   3944 				     client->host, get_remote_port(client->sock));
   3945 				CLIENT_UNLOCK;
   3946 				return(RFB_CLIENT_REFUSE);
   3947 			}
   3948 		}
   3949 	}
   3950 
   3951 	if (ipv6_client_ip_str != NULL) {
   3952 		rfbLog("renaming client->host from '%s' to '%s'\n",
   3953 		    client->host ? client->host : "", ipv6_client_ip_str);
   3954 		if (client->host) {
   3955 			free(client->host);
   3956 		}
   3957 		client->host = strdup(ipv6_client_ip_str);
   3958 	}
   3959 
   3960 	if (! check_access(client->host)) {
   3961 		rfbLog("denying client: %s does not match %s\n", client->host,
   3962 		    allow_list ? allow_list : "(null)" );
   3963 		CLIENT_UNLOCK;
   3964 		return(RFB_CLIENT_REFUSE);
   3965 	}
   3966 
   3967 	client->clientData = (void *) calloc(sizeof(ClientData), 1);
   3968 	cd = (ClientData *) client->clientData;
   3969 
   3970 	/* see client_set_net() we delay the DNS lookups during handshake */
   3971 	cd->client_port = -1;
   3972 	cd->username = strdup("");
   3973 	cd->unixname = strdup("");
   3974 
   3975 	cd->input[0] = '-';
   3976 	cd->login_viewonly = -1;
   3977 	cd->login_time = time(NULL);
   3978 	cd->ssl_helper_pid = 0;
   3979 
   3980 	if (use_openssl && openssl_last_helper_pid) {
   3981 		cd->ssl_helper_pid = openssl_last_helper_pid;
   3982 		openssl_last_helper_pid = 0;
   3983 	}
   3984 
   3985 	if (! accept_client(client)) {
   3986 		rfbLog("denying client: %s local user rejected connection.\n",
   3987 		    client->host);
   3988 		rfbLog("denying client: accept_cmd=\"%s\"\n",
   3989 		    accept_cmd ? accept_cmd : "(null)" );
   3990 
   3991 		free_client_data(client);
   3992 
   3993 		CLIENT_UNLOCK;
   3994 		return(RFB_CLIENT_REFUSE);
   3995 	}
   3996 
   3997 	/* We will RFB_CLIENT_ACCEPT or RFB_CLIENT_ON_HOLD from here on. */
   3998 
   3999 	if (passwdfile) {
   4000 		if (strstr(passwdfile, "read:") == passwdfile ||
   4001 		    strstr(passwdfile, "cmd:") == passwdfile) {
   4002 			if (read_passwds(passwdfile)) {
   4003 				install_passwds();
   4004 			} else {
   4005 				rfbLog("problem reading: %s\n", passwdfile);
   4006 				clean_up_exit(1);
   4007 			}
   4008 		} else if (strstr(passwdfile, "custom:") == passwdfile) {
   4009 			if (screen) {
   4010 				/* mutex */
   4011 				screen->passwordCheck = custom_passwd_check;
   4012 			}
   4013 		}
   4014 	}
   4015 
   4016 	cd->uid = clients_served;
   4017 
   4018 	client->clientGoneHook = client_gone;
   4019 
   4020 	if (client_count) {
   4021 		speeds_net_rate_measured = 0;
   4022 		speeds_net_latency_measured = 0;
   4023 	}
   4024 	client_count++;
   4025 
   4026 	last_keyboard_input = last_pointer_input = time(NULL);
   4027 
   4028 	if (no_autorepeat && client_count == 1 && ! view_only) {
   4029 		/*
   4030 		 * first client, turn off X server autorepeat
   4031 		 * XXX handle dynamic change of view_only and per-client.
   4032 		 */
   4033 		autorepeat(0, 0);
   4034 	}
   4035 #ifdef MACOSX
   4036 	if (macosx_console && client_count == 1) {
   4037 		macosxCG_refresh_callback_on();
   4038 	}
   4039 #endif
   4040 	if (use_solid_bg && client_count == 1) {
   4041 		solid_bg(0);
   4042 	}
   4043 
   4044 	if (pad_geometry) {
   4045 		install_padded_fb(pad_geometry);
   4046 	}
   4047 
   4048 	cd->timer = last_new_client = dnow();
   4049 	cd->send_cmp_rate = 0.0;
   4050 	cd->send_raw_rate = 0.0;
   4051 	cd->latency = 0.0;
   4052 	cd->cmp_bytes_sent = 0;
   4053 	cd->raw_bytes_sent = 0;
   4054 
   4055 	accepted_client++;
   4056 	rfbLog("incr accepted_client=%d for %s:%d  sock=%d\n", accepted_client,
   4057 	    client->host, get_remote_port(client->sock), client->sock);
   4058 	last_client = time(NULL);
   4059 
   4060 	if (ncache) {
   4061 		check_ncache(1, 0);
   4062 	}
   4063 
   4064 	if (advertise_truecolor && indexed_color) {
   4065 		int rs = 0, gs = 2, bs = 4;
   4066 		int rm = 3, gm = 3, bm = 3;
   4067 		if (bpp >= 24) {
   4068 			rs = 0, gs = 8, bs = 16;
   4069 			rm = 255, gm = 255, bm = 255;
   4070 		} else if (bpp >= 16) {
   4071 			rs = 0, gs = 5, bs = 10;
   4072 			rm = 31, gm = 31, bm = 31;
   4073 		}
   4074 		rfbLog("advertising truecolor.\n");
   4075 		if (getenv("ADVERT_BMSHIFT")) {
   4076 			bm--;
   4077 		}
   4078 
   4079 		if (use_threads) LOCK(client->updateMutex);
   4080 
   4081 		client->format.trueColour = TRUE;
   4082 		client->format.redShift   = rs;
   4083 		client->format.greenShift = gs;
   4084 		client->format.blueShift  = bs;
   4085 		client->format.redMax     = rm;
   4086 		client->format.greenMax   = gm;
   4087 		client->format.blueMax    = bm;
   4088 
   4089 		if (use_threads) UNLOCK(client->updateMutex);
   4090 
   4091 		rfbSetTranslateFunction(client);
   4092 
   4093 		/* mutex */
   4094 		screen->serverFormat.trueColour = TRUE;
   4095 		screen->serverFormat.redShift   = rs;
   4096 		screen->serverFormat.greenShift = gs;
   4097 		screen->serverFormat.blueShift  = bs;
   4098 		screen->serverFormat.redMax     = rm;
   4099 		screen->serverFormat.greenMax   = gm;
   4100 		screen->serverFormat.blueMax    = bm;
   4101 		screen->displayHook = turn_off_truecolor_ad;
   4102 
   4103 		turn_off_truecolor = 1;
   4104 	}
   4105 
   4106 	if (unixpw) {
   4107 		unixpw_in_progress = 1;
   4108 		unixpw_client = client;
   4109 		unixpw_login_viewonly = 0;
   4110 
   4111 		unixpw_file_xfer_save = screen->permitFileTransfer;
   4112 		screen->permitFileTransfer = FALSE;
   4113 		unixpw_tightvnc_xfer_save = tightfilexfer;
   4114 		tightfilexfer = 0;
   4115 #ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
   4116 		rfbLog("rfbUnregisterTightVNCFileTransferExtension: 1\n");
   4117 		rfbUnregisterTightVNCFileTransferExtension();
   4118 #endif
   4119 
   4120 		if (client->viewOnly) {
   4121 			unixpw_login_viewonly = 1;
   4122 			client->viewOnly = FALSE;
   4123 		}
   4124 		unixpw_last_try_time = time(NULL) + 10;
   4125 
   4126 		unixpw_screen(1);
   4127 		unixpw_keystroke(0, 0, 1);
   4128 
   4129 		if (!unixpw_in_rfbPE) {
   4130 			rfbLog("new client: %s in non-unixpw_in_rfbPE.\n",
   4131 			     client->host);
   4132 		}
   4133 		CLIENT_UNLOCK;
   4134 		if (!use_threads) {
   4135 			/* always put client on hold even if unixpw_in_rfbPE is true */
   4136 			return(RFB_CLIENT_ON_HOLD);
   4137 		} else {
   4138 			/* unixpw threads is still in testing mode, disabled by default. See UNIXPW_THREADS */
   4139 			return(RFB_CLIENT_ACCEPT);
   4140 		}
   4141 	}
   4142 
   4143 	CLIENT_UNLOCK;
   4144 	return(RFB_CLIENT_ACCEPT);
   4145 }
   4146 
   4147 void start_client_info_sock(char *host_port_cookie) {
   4148 	char *host = NULL, *cookie = NULL, *p;
   4149 	char *str = strdup(host_port_cookie);
   4150 	int i, port, sock, next = -1;
   4151 	static time_t start_time[ICON_MODE_SOCKS];
   4152 	time_t oldest = 0;
   4153 	int db = 0;
   4154 
   4155 	port = -1;
   4156 
   4157 	for (i = 0; i < ICON_MODE_SOCKS; i++) {
   4158 		if (icon_mode_socks[i] < 0) {
   4159 			next = i;
   4160 			break;
   4161 		}
   4162 		if (oldest == 0 || start_time[i] < oldest) {
   4163 			next = i;
   4164 			oldest = start_time[i];
   4165 		}
   4166 	}
   4167 
   4168 	p = strtok(str, ":");
   4169 	i = 0;
   4170 	while (p) {
   4171 		if (i == 0) {
   4172 			host = strdup(p);
   4173 		} else if (i == 1) {
   4174 			port = atoi(p);
   4175 		} else if (i == 2) {
   4176 			cookie = strdup(p);
   4177 		}
   4178 		i++;
   4179 		p = strtok(NULL, ":");
   4180 	}
   4181 	free(str);
   4182 
   4183 	if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next);
   4184 
   4185 	if (host && port && cookie) {
   4186 		if (*host == '\0') {
   4187 			free(host);
   4188 			host = strdup("localhost");
   4189 		}
   4190 		sock = connect_tcp(host, port);
   4191 		if (sock < 0) {
   4192 			usleep(200 * 1000);
   4193 			sock = connect_tcp(host, port);
   4194 		}
   4195 		if (sock >= 0) {
   4196 			char *lst = list_clients();
   4197 			icon_mode_socks[next] = sock;
   4198 			start_time[next] = time(NULL);
   4199 			write(sock, "COOKIE:", strlen("COOKIE:"));
   4200 			write(sock, cookie, strlen(cookie));
   4201 			write(sock, "\n", strlen("\n"));
   4202 			write(sock, "none\n", strlen("none\n"));
   4203 			write(sock, "none\n", strlen("none\n"));
   4204 			write(sock, lst, strlen(lst));
   4205 			write(sock, "\n", strlen("\n"));
   4206 			if (db) {
   4207 				fprintf(stderr, "list: %s\n", lst);
   4208 			}
   4209 			free(lst);
   4210 			rfbLog("client_info_sock to: %s:%d\n", host, port);
   4211 		} else {
   4212 			rfbLog("failed client_info_sock: %s:%d\n", host, port);
   4213 		}
   4214 	} else {
   4215 		rfbLog("malformed client_info_sock: %s\n", host_port_cookie);
   4216 	}
   4217 
   4218 	if (host) free(host);
   4219 	if (cookie) free(cookie);
   4220 }
   4221 
   4222 void send_client_info(char *str) {
   4223 	int i;
   4224 	static char *pstr = NULL;
   4225 	static int len = 128;
   4226 
   4227 	if (!str || strlen(str) == 0) {
   4228 		return;
   4229 	}
   4230 
   4231 	if (!pstr)  {
   4232 		pstr = (char *)malloc(len);
   4233 	}
   4234 	if (strlen(str) + 2 > (size_t) len) {
   4235 		free(pstr);
   4236 		len *= 2;
   4237 		pstr = (char *)malloc(len);
   4238 	}
   4239 	strcpy(pstr, str);
   4240 	strcat(pstr, "\n");
   4241 
   4242 	if (icon_mode_fh) {
   4243 		if (0) fprintf(icon_mode_fh, "\n");
   4244 		fprintf(icon_mode_fh, "%s", pstr);
   4245 		fflush(icon_mode_fh);
   4246 	}
   4247 
   4248 	for (i=0; i<ICON_MODE_SOCKS; i++) {
   4249 		int len, n, sock = icon_mode_socks[i];
   4250 		char *buf = pstr;
   4251 
   4252 		if (sock < 0) {
   4253 			continue;
   4254 		}
   4255 
   4256 		len = strlen(pstr);
   4257 		while (len > 0) {
   4258 			if (0) write(sock, "\n", 1);
   4259 			n = write(sock, buf, len);
   4260 			if (n > 0) {
   4261 				buf += n;
   4262 				len -= n;
   4263 				continue;
   4264 			}
   4265 
   4266 			if (n < 0 && errno == EINTR) {
   4267 				continue;
   4268 			}
   4269 			close(sock);
   4270 			icon_mode_socks[i] = -1;
   4271 			break;
   4272 		}
   4273 	}
   4274 }
   4275 
   4276 void adjust_grabs(int grab, int quiet) {
   4277 	RAWFB_RET_VOID
   4278 #if NO_X11
   4279 	if (!grab || !quiet) {}
   4280 	return;
   4281 #else
   4282 	/* n.b. caller decides to X_LOCK or not. */
   4283 	if (grab) {
   4284 		if (grab_kbd) {
   4285 			if (! quiet) {
   4286 				rfbLog("grabbing keyboard with XGrabKeyboard\n");
   4287 			}
   4288 			XGrabKeyboard(dpy, window, False, GrabModeAsync,
   4289 			    GrabModeAsync, CurrentTime);
   4290 		}
   4291 		if (grab_ptr) {
   4292 			if (! quiet) {
   4293 				rfbLog("grabbing pointer with XGrabPointer\n");
   4294 			}
   4295 			XGrabPointer(dpy, window, False, 0, GrabModeAsync,
   4296 			    GrabModeAsync, None, None, CurrentTime);
   4297 		}
   4298 	} else {
   4299 		if (grab_kbd) {
   4300 			if (! quiet) {
   4301 				rfbLog("ungrabbing keyboard with XUngrabKeyboard\n");
   4302 			}
   4303 			XUngrabKeyboard(dpy, CurrentTime);
   4304 		}
   4305 		if (grab_ptr) {
   4306 			if (! quiet) {
   4307 				rfbLog("ungrabbing pointer with XUngrabPointer\n");
   4308 			}
   4309 			XUngrabPointer(dpy, CurrentTime);
   4310 		}
   4311 	}
   4312 #endif	/* NO_X11 */
   4313 }
   4314 
   4315 void check_new_clients(void) {
   4316 	static int last_count = -1;
   4317 	rfbClientIteratorPtr iter;
   4318 	rfbClientPtr cl;
   4319 	int i, send_info = 0;
   4320 	int run_after_accept = 0;
   4321 
   4322 	if (unixpw_in_progress) {
   4323 		static double lping = 0.0;
   4324 		if (lping < dnow() + 5) {
   4325 			mark_rect_as_modified(0, 0, 1, 1, 1);
   4326 			lping = dnow();
   4327 		}
   4328 		if (unixpw_client && unixpw_client->viewOnly) {
   4329 			unixpw_login_viewonly = 1;
   4330 			unixpw_client->viewOnly = FALSE;
   4331 		}
   4332 		if (time(NULL) > unixpw_last_try_time + 45) {
   4333 			rfbLog("unixpw_deny: timed out waiting for reply.\n");
   4334 			unixpw_deny();
   4335 		}
   4336 		return;
   4337 	}
   4338 
   4339 	if (grab_always) {
   4340 		;
   4341 	} else if (grab_kbd || grab_ptr) {
   4342 		static double last_force = 0.0;
   4343 		if (client_count != last_count || dnow() > last_force + 0.25) {
   4344 			int q = (client_count == last_count);
   4345 			last_force = dnow();
   4346 			X_LOCK;
   4347 			if (client_count) {
   4348 				adjust_grabs(1, q);
   4349 			} else {
   4350 				adjust_grabs(0, q);
   4351 			}
   4352 			X_UNLOCK;
   4353 		}
   4354 	}
   4355 
   4356 	if (last_count == -1) {
   4357 		last_count = 0;
   4358 	} else if (client_count == last_count) {
   4359 		return;
   4360 	}
   4361 
   4362 	if (! all_clients_initialized()) {
   4363 		return;
   4364 	}
   4365 
   4366 	if (client_count > last_count) {
   4367 		if (afteraccept_cmd != NULL && afteraccept_cmd[0] != '\0') {
   4368 			run_after_accept = 1;
   4369 		}
   4370 	}
   4371 
   4372 	last_count = client_count;
   4373 
   4374 	if (! screen) {
   4375 		return;
   4376 	}
   4377 
   4378 	if (! client_count) {
   4379 		send_client_info("clients:none");
   4380 		return;
   4381 	}
   4382 
   4383 	iter = rfbGetClientIterator(screen);
   4384 	while( (cl = rfbClientIteratorNext(iter)) ) {
   4385 		ClientData *cd = (ClientData *) cl->clientData;
   4386 		char *s;
   4387 
   4388 		client_set_net(cl);
   4389 		if (! cd) {
   4390 			continue;
   4391 		}
   4392 
   4393 		if (cd->login_viewonly < 0) {
   4394 			/* this is a general trigger to initialize things */
   4395 			if (cl->viewOnly) {
   4396 				cd->login_viewonly = 1;
   4397 				s = allowed_input_view_only;
   4398 				if (s && cd->input[0] == '-') {
   4399 					cl->viewOnly = FALSE;
   4400 					cd->input[0] = '\0';
   4401 					strncpy(cd->input, s, CILEN);
   4402 				}
   4403 			} else {
   4404 				cd->login_viewonly = 0;
   4405 				s = allowed_input_normal;
   4406 				if (s && cd->input[0] == '-') {
   4407 					cd->input[0] = '\0';
   4408 					strncpy(cd->input, s, CILEN);
   4409 				}
   4410 			}
   4411 			if (run_after_accept) {
   4412 				run_user_command(afteraccept_cmd, cl,
   4413 				    "afteraccept", NULL, 0, NULL);
   4414 			}
   4415 		}
   4416 	}
   4417 	rfbReleaseClientIterator(iter);
   4418 
   4419 	if (icon_mode_fh) {
   4420 		send_info++;
   4421 	}
   4422 	for (i = 0; i < ICON_MODE_SOCKS; i++) {
   4423 		if (send_info || icon_mode_socks[i] >= 0) {
   4424 			send_info++;
   4425 			break;
   4426 		}
   4427 	}
   4428 	if (send_info) {
   4429 		char *str, *s = list_clients();
   4430 		str = (char *) malloc(strlen("clients:") + strlen(s) + 1);
   4431 		sprintf(str, "clients:%s", s);
   4432 		send_client_info(str);
   4433 		free(str);
   4434 		free(s);
   4435 	}
   4436 }
   4437 
   4438