1/*
2   Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3   All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables.  You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL".  If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so.  If you do not wish to do
30so, 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 */
63char vnc_connect_str[VNC_CONNECT_MAX+1];
64Atom vnc_connect_prop = None;
65char x11vnc_remote_str[X11VNC_REMOTE_MAX+1];
66Atom x11vnc_remote_prop = None;
67rfbClientPtr inetd_client = NULL;
68
69int all_clients_initialized(void);
70char *list_clients(void);
71int new_fb_size_clients(rfbScreenInfoPtr s);
72void close_all_clients(void);
73void close_clients(char *str);
74void set_client_input(char *str);
75void set_child_info(void);
76int cmd_ok(char *cmd);
77void client_gone(rfbClientPtr client);
78void client_gone_chat_helper(rfbClientPtr client);
79void reverse_connect(char *str);
80void set_vnc_connect_prop(char *str);
81void read_vnc_connect_prop(int);
82void set_x11vnc_remote_prop(char *str);
83void read_x11vnc_remote_prop(int);
84void check_connect_inputs(void);
85void check_gui_inputs(void);
86rfbClientPtr create_new_client(int sock, int start_thread);
87enum rfbNewClientAction new_client(rfbClientPtr client);
88enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
89rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
90void start_client_info_sock(char *host_port_cookie);
91void send_client_info(char *str);
92void adjust_grabs(int grab, int quiet);
93void check_new_clients(void);
94int accept_client(rfbClientPtr client);
95void check_ipv6_listen(long usec);
96void check_unix_sock(long usec);
97int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
98    int len, FILE *output);
99int check_access(char *addr);
100void client_set_net(rfbClientPtr client);
101char *get_xprop(char *prop, Window win);
102int set_xprop(char *prop, Window win, char *value);
103char *bcx_xattach(char *str, int *pg_init, int *kg_init);
104void grab_state(int *ptr_grabbed, int *kbd_grabbed);
105char *wininfo(Window win, int show_children);
106
107static rfbClientPtr *client_match(char *str);
108static void free_client_data(rfbClientPtr client);
109static void ugly_geom(char *p, int *x, int *y);
110static int ugly_window(char *addr, char *userhost, int X, int Y,
111    int timeout, char *mode, int accept);
112static int action_match(char *action, int rc);
113static void check_connect_file(char *file);
114static void send_client_connect(void);
115
116
117/*
118 * check that all clients are in RFB_NORMAL state
119 */
120int 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
142char *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 */
230int 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
249void 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
265static 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
356void 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
379void 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
415void 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
434int 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 */
464int 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
763static 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
792static int accepted_client = 0;
793
794/*
795 * callback for when a client disconnects
796 */
797void 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 */
950int 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 */
1128static 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
1139static 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 */
1453static 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
1537static 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 */
1573int 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
1727void 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
1841void 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 */
1908static 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
1992static 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
2083static 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
2185static int pxy_get_sock;
2186
2187static 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
2314static 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
2530char *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
2612static 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
2626static 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 */
2833void 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 */
3012void 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
3023void 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
3034void 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
3086void 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
3156extern int rc_npieces;
3157
3158void 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
3206static 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
3219char *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
3504int 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
3531char *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
3587static char _win_fmt[1000];
3588
3589static 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
3597char *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 */
3665static 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 */
3686void 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
3714void 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
3806rfbClientPtr 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
3827static int turn_off_truecolor = 0;
3828
3829static 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
3851rfbBool 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
3870enum 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
3877void client_gone_chat_helper(rfbClientPtr client) {
3878	if (client) {}
3879	rfbLog("finished chat helper\n");
3880	chat_window_client = NULL;
3881}
3882
3883void 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 */
3904enum 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
4147void 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
4222void 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
4276void 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
4315void 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