summaryrefslogtreecommitdiffstats
path: root/x11vnc/connections.c
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc/connections.c')
-rw-r--r--x11vnc/connections.c1859
1 files changed, 1859 insertions, 0 deletions
diff --git a/x11vnc/connections.c b/x11vnc/connections.c
new file mode 100644
index 0000000..addd215
--- /dev/null
+++ b/x11vnc/connections.c
@@ -0,0 +1,1859 @@
+/* -- connections.c -- */
+
+#include "x11vnc.h"
+#include "inet.h"
+#include "remote.h"
+#include "keyboard.h"
+#include "cleanup.h"
+#include "gui.h"
+#include "solid.h"
+#include "rates.h"
+#include "screen.h"
+
+/*
+ * routines for handling incoming, outgoing, etc connections
+ */
+
+/* string for the VNC_CONNECT property */
+char vnc_connect_str[VNC_CONNECT_MAX+1];
+Atom vnc_connect_prop = None;
+
+int all_clients_initialized(void);
+char *list_clients(void);
+int new_fb_size_clients(rfbScreenInfoPtr s);
+void close_all_clients(void);
+void close_clients(char *str);
+void set_client_input(char *str);
+void set_child_info(void);
+void reverse_connect(char *str);
+void set_vnc_connect_prop(char *str);
+void read_vnc_connect_prop(void);
+void check_connect_inputs(void);
+void check_gui_inputs(void);
+enum rfbNewClientAction new_client(rfbClientPtr client);
+void start_client_info_sock(char *host_port_cookie);
+void send_client_info(char *str);
+void check_new_clients(void);
+
+
+static rfbClientPtr *client_match(char *str);
+static int run_user_command(char *cmd, rfbClientPtr client, char *mode);
+static void client_gone(rfbClientPtr client);
+static int check_access(char *addr);
+static int ugly_accept_window(char *addr, char *userhost, int X, int Y,
+ int timeout, char *mode);
+static int action_match(char *action, int rc);
+static int accept_client(rfbClientPtr client);
+static void check_connect_file(char *file);
+static void send_client_connect(void);
+
+
+/*
+ * check that all clients are in RFB_NORMAL state
+ */
+int all_clients_initialized(void) {
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl;
+ int ok = 1;
+
+ if (! screen) {
+ return ok;
+ }
+
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ if (cl->state != RFB_NORMAL) {
+ ok = 0;
+ break;
+ }
+ }
+ rfbReleaseClientIterator(iter);
+
+ return ok;
+}
+
+char *list_clients(void) {
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl;
+ char *list, tmp[32];
+ int count = 0;
+
+ if (!screen) {
+ return strdup("");
+ }
+
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ count++;
+ }
+ rfbReleaseClientIterator(iter);
+
+ /*
+ * each client:
+ * <id>:<ip>:<port>:<user>:<hostname>:<input>:<loginview>,
+ * 8+1+16+1+5+1+24+1+256+1+5+1+1+1
+ * 123.123.123.123:60000/0x11111111-rw,
+ * so count+1 * 400 must cover it.
+ */
+ list = (char *) malloc((count+1)*400);
+
+ list[0] = '\0';
+
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ ClientData *cd = (ClientData *) cl->clientData;
+ if (*list != '\0') {
+ strcat(list, ",");
+ }
+ sprintf(tmp, "0x%x:", cd->uid);
+ strcat(list, tmp);
+ strcat(list, cl->host);
+ strcat(list, ":");
+ sprintf(tmp, "%d:", cd->client_port);
+ strcat(list, tmp);
+ if (*(cd->username) == '\0') {
+ char *s = ident_username(cl);
+ if (s) free(s);
+ }
+ strcat(list, cd->username);
+ strcat(list, ":");
+ strcat(list, cd->hostname);
+ strcat(list, ":");
+ strcat(list, cd->input);
+ strcat(list, ":");
+ sprintf(tmp, "%d", cd->login_viewonly);
+ strcat(list, tmp);
+ }
+ rfbReleaseClientIterator(iter);
+ return list;
+}
+
+/* count number of clients supporting NewFBSize */
+int new_fb_size_clients(rfbScreenInfoPtr s) {
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl;
+ int count = 0;
+
+ if (! s) {
+ return 0;
+ }
+
+ iter = rfbGetClientIterator(s);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ if (cl->useNewFBSize) {
+ count++;
+ }
+ }
+ rfbReleaseClientIterator(iter);
+ return count;
+}
+
+void close_all_clients(void) {
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl;
+
+ if (! screen) {
+ return;
+ }
+
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ rfbCloseClient(cl);
+ rfbClientConnectionGone(cl);
+ }
+ rfbReleaseClientIterator(iter);
+}
+
+static rfbClientPtr *client_match(char *str) {
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl, *cl_list;
+ int i, n, host_warn = 0, hex_warn = 0;
+
+ n = client_count + 10;
+ cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr));
+
+ i = 0;
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ if (strstr(str, "0x") == str) {
+ unsigned int in;
+ int id;
+ ClientData *cd = (ClientData *) cl->clientData;
+ if (sscanf(str, "0x%x", &in) != 1) {
+ if (hex_warn++) {
+ continue;
+ }
+ rfbLog("skipping invalid client hex id: %s\n",
+ str);
+ continue;
+ }
+ id = (unsigned int) in;
+ if (cd->uid == id) {
+ cl_list[i++] = cl;
+ }
+ } else {
+ char *rstr = str;
+ if (! dotted_ip(str)) {
+ rstr = host2ip(str);
+ if (rstr == NULL || *rstr == '\0') {
+ if (host_warn++) {
+ continue;
+ }
+ rfbLog("skipping bad lookup: \"%s\"\n",
+ str);
+ continue;
+ }
+ rfbLog("lookup: %s -> %s\n", str, rstr);
+ }
+ if (!strcmp(rstr, cl->host)) {
+ cl_list[i++] = cl;
+ }
+ if (rstr != str) {
+ free(rstr);
+ }
+ }
+ if (i >= n - 1) {
+ break;
+ }
+ }
+ rfbReleaseClientIterator(iter);
+
+ cl_list[i] = NULL;
+
+ return cl_list;
+}
+
+void close_clients(char *str) {
+ rfbClientPtr *cl_list, *cp;
+
+ if (!strcmp(str, "all") || !strcmp(str, "*")) {
+ close_all_clients();
+ return;
+ }
+
+ if (! screen) {
+ return;
+ }
+
+ cl_list = client_match(str);
+
+ cp = cl_list;
+ while (*cp) {
+ rfbCloseClient(*cp);
+ rfbClientConnectionGone(*cp);
+ cp++;
+ }
+ free(cl_list);
+}
+
+void set_client_input(char *str) {
+ rfbClientPtr *cl_list, *cp;
+ char *p, *val;
+
+ /* str is "match:value" */
+
+ if (! screen) {
+ return;
+ }
+
+ p = strchr(str, ':');
+ if (! p) {
+ return;
+ }
+ *p = '\0';
+ p++;
+ val = short_kmb(p);
+
+ cl_list = client_match(str);
+
+ cp = cl_list;
+ while (*cp) {
+ ClientData *cd = (ClientData *) (*cp)->clientData;
+ cd->input[0] = '\0';
+ strcat(cd->input, "_");
+ strcat(cd->input, val);
+ cp++;
+ }
+
+ free(val);
+ free(cl_list);
+}
+
+void set_child_info(void) {
+ char pid[16];
+ /* set up useful environment for child process */
+ sprintf(pid, "%d", (int) getpid());
+ set_env("X11VNC_PID", pid);
+ if (program_name) {
+ /* e.g. for remote control -R */
+ set_env("X11VNC_PROG", program_name);
+ }
+ if (program_cmdline) {
+ set_env("X11VNC_CMDLINE", program_cmdline);
+ }
+ if (raw_fb_str) {
+ set_env("X11VNC_RAWFB_STR", raw_fb_str);
+ } else {
+ set_env("X11VNC_RAWFB_STR", "");
+ }
+}
+
+/*
+ * utility to run a user supplied command setting some RFB_ env vars.
+ * used by, e.g., accept_client() and client_gone()
+ */
+static int run_user_command(char *cmd, rfbClientPtr client, char *mode) {
+ char *old_display = NULL;
+ char *addr = client->host;
+ char str[100];
+ int rc;
+ ClientData *cd = (ClientData *) client->clientData;
+
+ if (addr == NULL || addr[0] == '\0') {
+ addr = "unknown-host";
+ }
+
+ /* set RFB_CLIENT_ID to semi unique id for command to use */
+ if (cd && cd->uid) {
+ sprintf(str, "0x%x", cd->uid);
+ } else {
+ /* not accepted yet: */
+ sprintf(str, "0x%x", clients_served);
+ }
+ set_env("RFB_CLIENT_ID", str);
+
+ /* set RFB_CLIENT_IP to IP addr for command to use */
+ set_env("RFB_CLIENT_IP", addr);
+
+ /* set RFB_X11VNC_PID to our pid for command to use */
+ sprintf(str, "%d", (int) getpid());
+ set_env("RFB_X11VNC_PID", str);
+
+ if (client->state == RFB_PROTOCOL_VERSION) {
+ set_env("RFB_STATE", "PROTOCOL_VERSION");
+ } else if (client->state == RFB_SECURITY_TYPE) {
+ set_env("RFB_STATE", "SECURITY_TYPE");
+ } else if (client->state == RFB_AUTHENTICATION) {
+ set_env("RFB_STATE", "AUTHENTICATION");
+ } else if (client->state == RFB_INITIALISATION) {
+ set_env("RFB_STATE", "INITIALISATION");
+ } else if (client->state == RFB_NORMAL) {
+ set_env("RFB_STATE", "NORMAL");
+ } else {
+ set_env("RFB_STATE", "UNKNOWN");
+ }
+
+ /* set RFB_CLIENT_PORT to peer port for command to use */
+ if (cd && cd->client_port > 0) {
+ sprintf(str, "%d", cd->client_port);
+ } else {
+ sprintf(str, "%d", get_remote_port(client->sock));
+ }
+ set_env("RFB_CLIENT_PORT", str);
+
+ set_env("RFB_MODE", mode);
+
+ /*
+ * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!)
+ * This will establish a 5-tuple (including tcp) the external
+ * program can potentially use to work out the virtual circuit
+ * for this connection.
+ */
+ if (cd && cd->server_ip) {
+ set_env("RFB_SERVER_IP", cd->server_ip);
+ } else {
+ char *sip = get_local_host(client->sock);
+ set_env("RFB_SERVER_IP", sip);
+ if (sip) free(sip);
+ }
+
+ if (cd && cd->server_port > 0) {
+ sprintf(str, "%d", cd->server_port);
+ } else {
+ sprintf(str, "%d", get_local_port(client->sock));
+ }
+ set_env("RFB_SERVER_PORT", str);
+
+ if (cd) {
+ sprintf(str, "%d", cd->login_viewonly);
+ } else {
+ sprintf(str, "%d", -1);
+ }
+ set_env("RFB_LOGIN_VIEWONLY", str);
+
+ if (cd) {
+ sprintf(str, "%d", (int) cd->login_time);
+ } else {
+ sprintf(str, ">%d", (int) time(0));
+ }
+ set_env("RFB_LOGIN_TIME", str);
+
+ sprintf(str, "%d", (int) time(0));
+ set_env("RFB_CURRENT_TIME", str);
+
+ if (!cd || !cd->username || cd->username[0] == '\0') {
+ set_env("RFB_USERNAME", "unknown-user");
+ } else {
+ set_env("RFB_USERNAME", cd->username);
+ }
+ /*
+ * Better set DISPLAY to the one we are polling, if they
+ * want something trickier, they can handle on their own
+ * via environment, etc.
+ */
+ if (getenv("DISPLAY")) {
+ old_display = strdup(getenv("DISPLAY"));
+ }
+
+ if (raw_fb && ! dpy) { /* raw_fb hack */
+ set_env("DISPLAY", "rawfb");
+ } else {
+ set_env("DISPLAY", DisplayString(dpy));
+ }
+
+ /*
+ * work out the number of clients (have to use client_count
+ * since there is deadlock in rfbGetClientIterator)
+ */
+ sprintf(str, "%d", client_count);
+ set_env("RFB_CLIENT_COUNT", str);
+
+ if (no_external_cmds) {
+ rfbLogEnable(1);
+ rfbLog("cannot run external commands in -nocmds mode:\n");
+ rfbLog(" \"%s\"\n", cmd);
+ rfbLog(" exiting.\n");
+ clean_up_exit(1);
+ }
+ rfbLog("running command:\n");
+ rfbLog(" %s\n", cmd);
+
+ /* XXX need to close port 5900, etc.. */
+ rc = system(cmd);
+
+ if (rc >= 256) {
+ rc = rc/256;
+ }
+ rfbLog("command returned: %d\n", rc);
+
+ if (old_display) {
+ set_env("DISPLAY", old_display);
+ free(old_display);
+ }
+
+ return rc;
+}
+
+static int accepted_client = 0;
+
+/*
+ * callback for when a client disconnects
+ */
+static void client_gone(rfbClientPtr client) {
+
+ client_count--;
+ if (client_count < 0) client_count = 0;
+
+ speeds_net_rate_measured = 0;
+ speeds_net_latency_measured = 0;
+
+ rfbLog("client_count: %d\n", client_count);
+
+ if (no_autorepeat && client_count == 0) {
+ autorepeat(1, 0);
+ }
+ if (use_solid_bg && client_count == 0) {
+ solid_bg(1);
+ }
+ if (gone_cmd && *gone_cmd != '\0') {
+ rfbLog("client_gone: using cmd for: %s\n", client->host);
+ run_user_command(gone_cmd, client, "gone");
+ }
+
+ if (client->clientData) {
+ ClientData *cd = (ClientData *) client->clientData;
+ if (cd) {
+ if (cd->server_ip) {
+ free(cd->server_ip);
+ cd->server_ip = NULL;
+ }
+ if (cd->hostname) {
+ free(cd->hostname);
+ cd->hostname = NULL;
+ }
+ if (cd->username) {
+ free(cd->username);
+ cd->username = NULL;
+ }
+ }
+ free(client->clientData);
+ client->clientData = NULL;
+ }
+
+ if (inetd) {
+ rfbLog("viewer exited.\n");
+ clean_up_exit(0);
+ }
+ if (connect_once) {
+ /*
+ * This non-exit is done for a bad passwd to be consistent
+ * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e.
+ * we disconnect after 1 successful connection).
+ */
+ if ((client->state == RFB_PROTOCOL_VERSION ||
+ client->state == RFB_AUTHENTICATION) && accepted_client) {
+ rfbLog("connect_once: invalid password or early "
+ "disconnect.\n");
+ rfbLog("connect_once: waiting for next connection.\n");
+ accepted_client = 0;
+ return;
+ }
+ if (shared && client_count > 0) {
+ rfbLog("connect_once: other shared clients still "
+ "connected, not exiting.\n");
+ return;
+ }
+
+ rfbLog("viewer exited.\n");
+ clean_up_exit(0);
+ }
+}
+
+/*
+ * Simple routine to limit access via string compare. A power user will
+ * want to compile libvncserver with libwrap support and use /etc/hosts.allow.
+ */
+static int check_access(char *addr) {
+ int allowed = 0;
+ char *p, *list;
+
+ if (deny_all) {
+ rfbLog("check_access: new connections are currently "
+ "blocked.\n");
+ return 0;
+ }
+ if (addr == NULL || *addr == '\0') {
+ rfbLog("check_access: denying empty host IP address string.\n");
+ return 0;
+ }
+
+ if (allow_list == NULL) {
+ /* set to "" to possibly append allow_once */
+ allow_list = strdup("");
+ }
+ if (*allow_list == '\0' && allow_once == NULL) {
+ /* no constraints, accept it */
+ return 1;
+ }
+
+ if (strchr(allow_list, '/')) {
+ /* a file of IP addresess or prefixes */
+ int len, len2 = 0;
+ struct stat sbuf;
+ FILE *in;
+ char line[1024], *q;
+
+ if (stat(allow_list, &sbuf) != 0) {
+ rfbLogEnable(1);
+ rfbLog("check_access: failure stating file: %s\n",
+ allow_list);
+ rfbLogPerror("stat");
+ clean_up_exit(1);
+ }
+ len = sbuf.st_size + 1; /* 1 more for '\0' at end */
+ if (allow_once) {
+ len2 = strlen(allow_once) + 2;
+ len += len2;
+ }
+ list = (char *) malloc(len);
+ list[0] = '\0';
+
+ in = fopen(allow_list, "r");
+ if (in == NULL) {
+ rfbLogEnable(1);
+ rfbLog("check_access: cannot open: %s\n", allow_list);
+ rfbLogPerror("fopen");
+ clean_up_exit(1);
+ }
+ while (fgets(line, 1024, in) != NULL) {
+ if ( (q = strchr(line, '#')) != NULL) {
+ *q = '\0';
+ }
+ if (strlen(list) + strlen(line) >=
+ (size_t) (len - len2)) {
+ /* file grew since our stat() */
+ break;
+ }
+ strcat(list, line);
+ }
+ fclose(in);
+ if (allow_once) {
+ strcat(list, "\n");
+ strcat(list, allow_once);
+ strcat(list, "\n");
+ }
+ } else {
+ int len = strlen(allow_list) + 1;
+ if (allow_once) {
+ len += strlen(allow_once) + 1;
+ }
+ list = (char *) malloc(len);
+ list[0] = '\0';
+ strcat(list, allow_list);
+ if (allow_once) {
+ strcat(list, ",");
+ strcat(list, allow_once);
+ }
+ }
+
+ if (allow_once) {
+ free(allow_once);
+ allow_once = NULL;
+ }
+
+ p = strtok(list, ", \t\n\r");
+ while (p) {
+ char *chk, *q, *r = NULL;
+ if (*p == '\0') {
+ p = strtok(NULL, ", \t\n\r");
+ continue;
+ }
+ if (! dotted_ip(p)) {
+ r = host2ip(p);
+ if (r == NULL || *r == '\0') {
+ rfbLog("check_access: bad lookup \"%s\"\n", p);
+ p = strtok(NULL, ", \t\n\r");
+ continue;
+ }
+ rfbLog("check_access: lookup %s -> %s\n", p, r);
+ chk = r;
+ } else {
+ chk = p;
+ }
+
+ q = strstr(addr, chk);
+ if (chk[strlen(chk)-1] != '.') {
+ if (!strcmp(addr, chk)) {
+ if (chk != p) {
+ rfbLog("check_access: client %s "
+ "matches host %s=%s\n", addr,
+ chk, p);
+ } else {
+ rfbLog("check_access: client %s "
+ "matches host %s\n", addr, chk);
+ }
+ allowed = 1;
+ } else if(!strcmp(chk, "localhost") &&
+ !strcmp(addr, "127.0.0.1")) {
+ allowed = 1;
+ }
+ } else if (q == addr) {
+ rfbLog("check_access: client %s matches pattern %s\n",
+ addr, chk);
+ allowed = 1;
+ }
+ p = strtok(NULL, ", \t\n\r");
+ if (r) {
+ free(r);
+ }
+ if (allowed) {
+ break;
+ }
+ }
+ free(list);
+ return allowed;
+}
+
+/*
+ * x11vnc's first (and only) visible widget: accept/reject dialog window.
+ * We go through this pain to avoid dependency on libXt...
+ */
+static int ugly_accept_window(char *addr, char *userhost, int X, int Y,
+ int timeout, char *mode) {
+
+#define t2x2_width 16
+#define t2x2_height 16
+static unsigned char t2x2_bits[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff,
+ 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33,
+ 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33};
+
+ Window awin;
+ GC gc;
+ XSizeHints hints;
+ XGCValues values;
+ static XFontStruct *font_info = NULL;
+ static Pixmap ico = 0;
+ unsigned long valuemask = 0;
+ static char dash_list[] = {20, 40};
+ int list_length = sizeof(dash_list);
+
+ Atom wm_protocols;
+ Atom wm_delete_window;
+
+ XEvent ev;
+ long evmask = ExposureMask | KeyPressMask | ButtonPressMask
+ | StructureNotifyMask;
+ double waited = 0.0;
+
+ /* strings and geometries y/n */
+ KeyCode key_y, key_n, key_v;
+ char strh[100];
+ char stri[100];
+ char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button";
+ char str2_b[] = "To reject: press \"n\" or click the \"No\" button";
+ char str3_b[] = "View only: press \"v\" or click the \"View\" button";
+ char str1_m[] = "To accept: click the \"Yes\" button";
+ char str2_m[] = "To reject: click the \"No\" button";
+ char str3_m[] = "View only: click the \"View\" button";
+ char str1_k[] = "To accept: press \"y\"";
+ char str2_k[] = "To reject: press \"n\"";
+ char str3_k[] = "View only: press \"v\"";
+ char *str1, *str2, *str3;
+ char str_y[] = "Yes";
+ char str_n[] = "No";
+ char str_v[] = "View";
+ int x, y, w = 345, h = 175, ret = 0;
+ int X_sh = 20, Y_sh = 30, dY = 20;
+ int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20;
+ int No_x = 75, No_y = 0, No_w = 45, No_h = 20;
+ int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20;
+
+ if (raw_fb && ! dpy) return 0; /* raw_fb hack */
+
+ if (!strcmp(mode, "mouse_only")) {
+ str1 = str1_m;
+ str2 = str2_m;
+ str3 = str3_m;
+ } else if (!strcmp(mode, "key_only")) {
+ str1 = str1_k;
+ str2 = str2_k;
+ str3 = str3_k;
+ h -= dY;
+ } else {
+ str1 = str1_b;
+ str2 = str2_b;
+ str3 = str3_b;
+ }
+ if (view_only) {
+ h -= dY;
+ }
+
+ /* XXX handle coff_x/coff_y? */
+ if (X < -dpy_x) {
+ x = (dpy_x - w)/2; /* large negative: center */
+ if (x < 0) x = 0;
+ } else if (X < 0) {
+ x = dpy_x + X - w; /* from lower right */
+ } else {
+ x = X; /* from upper left */
+ }
+
+ if (Y < -dpy_y) {
+ y = (dpy_y - h)/2;
+ if (y < 0) y = 0;
+ } else if (Y < 0) {
+ y = dpy_y + Y - h;
+ } else {
+ y = Y;
+ }
+
+ X_LOCK;
+
+ awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4,
+ BlackPixel(dpy, scr), WhitePixel(dpy, scr));
+
+ wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(dpy, awin, &wm_delete_window, 1);
+
+ if (! ico) {
+ ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits,
+ t2x2_width, t2x2_height);
+ }
+
+ hints.flags = PPosition | PSize | PMinSize;
+ hints.x = x;
+ hints.y = y;
+ hints.width = w;
+ hints.height = h;
+ hints.min_width = w;
+ hints.min_height = h;
+
+ XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query",
+ ico, NULL, 0, &hints);
+
+ XSelectInput(dpy, awin, evmask);
+
+ if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) {
+ rfbLogEnable(1);
+ rfbLog("ugly_accept_window: cannot locate font fixed.\n");
+ X_UNLOCK;
+ clean_up_exit(1);
+ }
+
+ gc = XCreateGC(dpy, awin, valuemask, &values);
+ XSetFont(dpy, gc, font_info->fid);
+ XSetForeground(dpy, gc, BlackPixel(dpy, scr));
+ XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
+ XSetDashes(dpy, gc, 0, dash_list, list_length);
+
+ XMapWindow(dpy, awin);
+ XFlush(dpy);
+
+ snprintf(strh, 100, "x11vnc: accept connection from %s?", addr);
+ snprintf(stri, 100, " (%s)", userhost);
+ key_y = XKeysymToKeycode(dpy, XStringToKeysym("y"));
+ key_n = XKeysymToKeycode(dpy, XStringToKeysym("n"));
+ key_v = XKeysymToKeycode(dpy, XStringToKeysym("v"));
+
+ while (1) {
+ int out = -1, x, y, tw, k;
+
+ if (XCheckWindowEvent(dpy, awin, evmask, &ev)) {
+ ; /* proceed to handling */
+ } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) {
+ ; /* proceed to handling */
+ } else {
+ int ms = 100; /* sleep a bit */
+ usleep(ms * 1000);
+ waited += ((double) ms)/1000.;
+ if (timeout && (int) waited >= timeout) {
+ rfbLog("accept_client: popup timed out after "
+ "%d seconds.\n", timeout);
+ out = 0;
+ ev.type = 0;
+ } else {
+ continue;
+ }
+ }
+
+ switch(ev.type) {
+ case Expose:
+ while (XCheckTypedEvent(dpy, Expose, &ev)) {
+ ;
+ }
+ k=0;
+
+ /* instructions */
+ XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
+ strh, strlen(strh));
+ XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
+ stri, strlen(stri));
+ XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
+ str1, strlen(str1));
+ XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
+ str2, strlen(str2));
+ if (! view_only) {
+ XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
+ str3, strlen(str3));
+ }
+
+ if (!strcmp(mode, "key_only")) {
+ break;
+ }
+
+ /* buttons */
+ Ye_y = Y_sh+k*dY;
+ No_y = Y_sh+k*dY;
+ Vi_y = Y_sh+k*dY;
+ XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h);
+ XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h);
+ if (! view_only) {
+ XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y,
+ Vi_w, Vi_h);
+ }
+
+ tw = XTextWidth(font_info, str_y, strlen(str_y));
+ tw = (Ye_w - tw)/2;
+ if (tw < 0) tw = 1;
+ XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5,
+ str_y, strlen(str_y));
+
+ tw = XTextWidth(font_info, str_n, strlen(str_n));
+ tw = (No_w - tw)/2;
+ if (tw < 0) tw = 1;
+ XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5,
+ str_n, strlen(str_n));
+
+ if (! view_only) {
+ tw = XTextWidth(font_info, str_v,
+ strlen(str_v));
+ tw = (Vi_w - tw)/2;
+ if (tw < 0) tw = 1;
+ XDrawString(dpy, awin, gc, Vi_x+tw,
+ Vi_y+Vi_h-5, str_v, strlen(str_v));
+ }
+
+ break;
+
+ case ClientMessage:
+ if (ev.xclient.message_type == wm_protocols &&
+ (Atom) ev.xclient.data.l[0] == wm_delete_window) {
+ out = 0;
+ }
+ break;
+
+ case ButtonPress:
+ x = ev.xbutton.x;
+ y = ev.xbutton.y;
+ if (!strcmp(mode, "key_only")) {
+ ;
+ } else if (x > No_x && x < No_x+No_w && y > No_y
+ && y < No_y+No_h) {
+ out = 0;
+ } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y
+ && y < Ye_y+Ye_h) {
+ out = 1;
+ } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w
+ && y > Vi_y && y < Vi_y+Ye_h) {
+ out = 2;
+ }
+ break;
+
+ case KeyPress:
+ if (!strcmp(mode, "mouse_only")) {
+ ;
+ } else if (ev.xkey.keycode == key_y) {
+ out = 1;
+ } else if (ev.xkey.keycode == key_n) {
+ out = 0;
+ } else if (! view_only && ev.xkey.keycode == key_v) {
+ out = 2;
+ }
+ break;
+ default:
+ break;
+ }
+ if (out != -1) {
+ ret = out;
+ XSelectInput(dpy, awin, 0);
+ XUnmapWindow(dpy, awin);
+ XFreeGC(dpy, gc);
+ XDestroyWindow(dpy, awin);
+ XFlush(dpy);
+ break;
+ }
+ }
+ X_UNLOCK;
+
+ return ret;
+}
+
+/*
+ * process a "yes:0,no:*,view:3" type action list comparing to command
+ * return code rc. * means the default action with no other match.
+ */
+static int action_match(char *action, int rc) {
+ char *p, *q, *s = strdup(action);
+ int cases[4], i, result;
+ char *labels[4];
+
+ labels[1] = "yes";
+ labels[2] = "no";
+ labels[3] = "view";
+
+ rfbLog("accept_client: process action line: %s\n",
+ action);
+
+ for (i=1; i <= 3; i++) {
+ cases[i] = -2;
+ }
+
+ p = strtok(s, ",");
+ while (p) {
+ if ((q = strchr(p, ':')) != NULL) {
+ int in, k;
+ *q = '\0';
+ q++;
+ if (strstr(p, "yes") == p) {
+ k = 1;
+ } else if (strstr(p, "no") == p) {
+ k = 2;
+ } else if (strstr(p, "view") == p) {
+ k = 3;
+ } else {
+ rfbLogEnable(1);
+ rfbLog("invalid action line: %s\n", action);
+ clean_up_exit(1);
+ }
+ if (*q == '*') {
+ cases[k] = -1;
+ } else if (sscanf(q, "%d", &in) == 1) {
+ if (in < 0) {
+ rfbLogEnable(1);
+ rfbLog("invalid action line: %s\n",
+ action);
+ clean_up_exit(1);
+ }
+ cases[k] = in;
+ } else {
+ rfbLogEnable(1);
+ rfbLog("invalid action line: %s\n", action);
+ clean_up_exit(1);
+ }
+ } else {
+ rfbLogEnable(1);
+ rfbLog("invalid action line: %s\n", action);
+ clean_up_exit(1);
+ }
+ p = strtok(NULL, ",");
+ }
+ free(s);
+
+ result = -1;
+ for (i=1; i <= 3; i++) {
+ if (cases[i] == -1) {
+ rfbLog("accept_client: default action is case=%d %s\n",
+ i, labels[i]);
+ result = i;
+ break;
+ }
+ }
+ if (result == -1) {
+ rfbLog("accept_client: no default action\n");
+ }
+ for (i=1; i <= 3; i++) {
+ if (cases[i] >= 0 && cases[i] == rc) {
+ rfbLog("accept_client: matched action is case=%d %s\n",
+ i, labels[i]);
+ result = i;
+ break;
+ }
+ }
+ if (result < 0) {
+ rfbLog("no action match: %s rc=%d set to no\n", action, rc);
+ result = 2;
+ }
+ return result;
+}
+
+/*
+ * Simple routine to prompt the user on the X display whether an incoming
+ * client should be allowed to connect or not. If a gui is involved it
+ * will be running in the environment/context of the X11 DISPLAY.
+ *
+ * The command supplied via -accept is run as is (i.e. no string
+ * substitution) with the RFB_CLIENT_IP environment variable set to the
+ * incoming client's numerical IP address.
+ *
+ * If the external command exits with 0 the client is accepted, otherwise
+ * the client is rejected.
+ *
+ * Some builtins are provided:
+ *
+ * xmessage: use homebrew xmessage(1) for the external command.
+ * popup: use internal X widgets for prompting.
+ *
+ */
+static int accept_client(rfbClientPtr client) {
+
+ char xmessage[200], *cmd = NULL;
+ char *addr = client->host;
+ char *action = NULL;
+
+ if (accept_cmd == NULL || *accept_cmd == '\0') {
+ return 1; /* no command specified, so we accept */
+ }
+
+ if (addr == NULL || addr[0] == '\0') {
+ addr = "unknown-host";
+ }
+
+ if (strstr(accept_cmd, "popup") == accept_cmd) {
+ /* use our builtin popup button */
+
+ /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */
+
+ int ret, timeout = 120;
+ int x = -64000, y = -64000;
+ char *p, *mode;
+ char *userhost = ident_username(client);
+
+ /* extract timeout */
+ if ((p = strchr(accept_cmd, ':')) != NULL) {
+ int in;
+ if (sscanf(p+1, "%d", &in) == 1) {
+ timeout = in;
+ }
+ }
+ /* extract geometry */
+ if ((p = strpbrk(accept_cmd, "+-")) != NULL) {
+ int x1, y1;
+ if (sscanf(p, "+%d+%d", &x1, &y1) == 2) {
+ x = x1;
+ y = y1;
+ } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) {
+ x = x1;
+ y = -y1;
+ } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) {
+ x = -x1;
+ y = y1;
+ } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) {
+ x = -x1;
+ y = -y1;
+ }
+ }
+
+ /* find mode: mouse, key, or both */
+ if (strstr(accept_cmd, "popupmouse") == accept_cmd) {
+ mode = "mouse_only";
+ } else if (strstr(accept_cmd, "popupkey") == accept_cmd) {
+ mode = "key_only";
+ } else {
+ mode = "both";
+ }
+
+ rfbLog("accept_client: using builtin popup for: %s\n", addr);
+ if ((ret = ugly_accept_window(addr, userhost, x, y, timeout,
+ mode))) {
+ free(userhost);
+ if (ret == 2) {
+ rfbLog("accept_client: viewonly: %s\n", addr);
+ client->viewOnly = TRUE;
+ }
+ rfbLog("accept_client: popup accepted: %s\n", addr);
+ return 1;
+ } else {
+ free(userhost);
+ rfbLog("accept_client: popup rejected: %s\n", addr);
+ return 0;
+ }
+
+ } else if (!strcmp(accept_cmd, "xmessage")) {
+ /* make our own command using xmessage(1) */
+
+ if (view_only) {
+ sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center"
+ " 'x11vnc: accept connection from %s?'", addr);
+ } else {
+ sprintf(xmessage, "xmessage -buttons yes:0,no:2,"
+ "view-only:3 -center" " 'x11vnc: accept connection"
+ " from %s?'", addr);
+ action = "yes:0,no:*,view:3";
+ }
+ cmd = xmessage;
+
+ } else {
+ /* use the user supplied command: */
+
+ cmd = accept_cmd;
+
+ /* extract any action prefix: yes:N,no:M,view:K */
+ if (strstr(accept_cmd, "yes:") == accept_cmd) {
+ char *p;
+ if ((p = strpbrk(accept_cmd, " \t")) != NULL) {
+ int i;
+ cmd = p;
+ p = accept_cmd;
+ for (i=0; i<200; i++) {
+ if (*p == ' ' || *p == '\t') {
+ xmessage[i] = '\0';
+ break;
+ }
+ xmessage[i] = *p;
+ p++;
+ }
+ xmessage[200-1] = '\0';
+ action = xmessage;
+ }
+ }
+ }
+
+ if (cmd) {
+ int rc;
+
+ rfbLog("accept_client: using cmd for: %s\n", addr);
+ rc = run_user_command(cmd, client, "accept");
+
+ if (action) {
+ int result;
+
+ if (rc < 0) {
+ rfbLog("accept_client: cannot use negative "
+ "rc: %d, action %s\n", rc, action);
+ result = 2;
+ } else {
+ result = action_match(action, rc);
+ }
+
+ if (result == 1) {
+ rc = 0;
+ } else if (result == 2) {
+ rc = 1;
+ } else if (result == 3) {
+ rc = 0;
+ rfbLog("accept_client: viewonly: %s\n", addr);
+ client->viewOnly = TRUE;
+ } else {
+ rc = 1; /* NOTREACHED */
+ }
+ }
+
+ if (rc == 0) {
+ rfbLog("accept_client: accepted: %s\n", addr);
+ return 1;
+ } else {
+ rfbLog("accept_client: rejected: %s\n", addr);
+ return 0;
+ }
+ } else {
+ rfbLog("accept_client: no command, rejecting %s\n", addr);
+ return 0;
+ }
+
+ /* return 0; NOTREACHED */
+}
+
+/*
+ * For the -connect <file> option: periodically read the file looking for
+ * a connect string. If one is found set client_connect to it.
+ */
+static void check_connect_file(char *file) {
+ FILE *in;
+ char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX];
+ static int first_warn = 1, truncate_ok = 1;
+ static time_t last_time = 0;
+ time_t now = time(0);
+
+ if (last_time == 0) {
+ last_time = now;
+ }
+ if (now - last_time < 1) {
+ /* check only once a second */
+ return;
+ }
+ last_time = now;
+
+ if (! truncate_ok) {
+ /* check if permissions changed */
+ if (access(file, W_OK) == 0) {
+ truncate_ok = 1;
+ } else {
+ return;
+ }
+ }
+
+ in = fopen(file, "r");
+ if (in == NULL) {
+ if (first_warn) {
+ rfbLog("check_connect_file: fopen failure: %s\n", file);
+ rfbLogPerror("fopen");
+ first_warn = 0;
+ }
+ return;
+ }
+
+ if (fgets(line, VNC_CONNECT_MAX, in) != NULL) {
+ if (sscanf(line, "%s", host) == 1) {
+ if (strlen(host) > 0) {
+ char *str = strdup(host);
+ if (strlen(str) > 38) {
+ char trim[100];
+ trim[0] = '\0';
+ strncat(trim, str, 38);
+ rfbLog("read connect file: %s ...\n",
+ trim);
+ } else {
+ rfbLog("read connect file: %s\n", str);
+ }
+ client_connect = str;
+ }
+ }
+ }
+ fclose(in);
+
+ /* truncate file */
+ in = fopen(file, "w");
+ if (in != NULL) {
+ fclose(in);
+ } else {
+ /* disable if we cannot truncate */
+ rfbLog("check_connect_file: could not truncate %s, "
+ "disabling checking.\n", file);
+ truncate_ok = 0;
+ }
+}
+
+/*
+ * Do a reverse connect for a single "host" or "host:port"
+ */
+static int do_reverse_connect(char *str) {
+ rfbClientPtr cl;
+ char *host, *p;
+ int rport = 5500, len = strlen(str);
+
+ if (len < 1) {
+ return 0;
+ }
+ if (len > 1024) {
+ rfbLog("reverse_connect: string too long: %d bytes\n", len);
+ return 0;
+ }
+ if (!screen) {
+ rfbLog("reverse_connect: screen not setup yet.\n");
+ return 0;
+ }
+
+ /* copy in to host */
+ host = (char *) malloc(len+1);
+ if (! host) {
+ rfbLog("reverse_connect: could not malloc string %d\n", len);
+ return 0;
+ }
+ strncpy(host, str, len);
+ host[len] = '\0';
+
+ /* extract port, if any */
+ if ((p = strchr(host, ':')) != NULL) {
+ rport = atoi(p+1);
+ *p = '\0';
+ }
+
+ cl = rfbReverseConnection(screen, host, rport);
+ free(host);
+
+ if (cl == NULL) {
+ rfbLog("reverse_connect: %s failed\n", str);
+ return 0;
+ } else {
+ rfbLog("reverse_connect: %s/%s OK\n", str, cl->host);
+ return 1;
+ }
+}
+
+/*
+ * Break up comma separated list of hosts and call do_reverse_connect()
+ */
+void reverse_connect(char *str) {
+ char *p, *tmp = strdup(str);
+ int sleep_between_host = 300;
+ int sleep_min = 1500, sleep_max = 4500, n_max = 5;
+ int n, tot, t, dt = 100, cnt = 0;
+
+ p = strtok(tmp, ", \t\r\n");
+ while (p) {
+ if ((n = do_reverse_connect(p)) != 0) {
+ rfbPE(-1);
+ }
+ cnt += n;
+
+ p = strtok(NULL, ", \t\r\n");
+ if (p) {
+ t = 0;
+ while (t < sleep_between_host) {
+ usleep(dt * 1000);
+ rfbPE(-1);
+ t += dt;
+ }
+ }
+ }
+ free(tmp);
+
+ if (cnt == 0) {
+ return;
+ }
+
+ /*
+ * XXX: we need to process some of the initial handshaking
+ * events, otherwise the client can get messed up (why??)
+ * so we send rfbProcessEvents() all over the place.
+ */
+
+ n = cnt;
+ if (n >= n_max) {
+ n = n_max;
+ }
+ t = sleep_max - sleep_min;
+ tot = sleep_min + ((n-1) * t) / (n_max-1);
+
+ t = 0;
+ while (t < tot) {
+ rfbPE(-1);
+ usleep(dt * 1000);
+ t += dt;
+ }
+}
+
+/*
+ * Routines for monitoring the VNC_CONNECT property for changes.
+ * The vncconnect(1) will set it on our X display.
+ */
+void set_vnc_connect_prop(char *str) {
+ XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8,
+ PropModeReplace, (unsigned char *)str, strlen(str));
+}
+
+void read_vnc_connect_prop(void) {
+ Atom type;
+ int format, slen, dlen;
+ unsigned long nitems = 0, bytes_after = 0;
+ unsigned char* data = NULL;
+ int db = 1;
+
+ vnc_connect_str[0] = '\0';
+ slen = 0;
+
+ if (! vnc_connect || vnc_connect_prop == None) {
+ /* not active or problem with VNC_CONNECT atom */
+ return;
+ }
+
+ /* read the property value into vnc_connect_str: */
+ do {
+ if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
+ vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False,
+ AnyPropertyType, &type, &format, &nitems, &bytes_after,
+ &data) == Success) {
+
+ dlen = nitems * (format/8);
+ if (slen + dlen > VNC_CONNECT_MAX) {
+ /* too big */
+ rfbLog("warning: truncating large VNC_CONNECT"
+ " string > %d bytes.\n", VNC_CONNECT_MAX);
+ XFree(data);
+ break;
+ }
+ memcpy(vnc_connect_str+slen, data, dlen);
+ slen += dlen;
+ vnc_connect_str[slen] = '\0';
+ XFree(data);
+ }
+ } while (bytes_after > 0);
+
+ vnc_connect_str[VNC_CONNECT_MAX] = '\0';
+ if (! db) {
+ ;
+ } else if (strstr(vnc_connect_str, "ans=stop:N/A,ans=quit:N/A,ans=")) {
+ ;
+ } else if (strstr(vnc_connect_str, "qry=stop,quit,exit")) {
+ ;
+ } else if (strstr(vnc_connect_str, "ack=") == vnc_connect_str) {
+ ;
+ } else if (quiet && strstr(vnc_connect_str, "qry=ping") ==
+ vnc_connect_str) {
+ ;
+ } else if (strstr(vnc_connect_str, "cmd=") &&
+ strstr(vnc_connect_str, "passwd")) {
+ rfbLog("read VNC_CONNECT: *\n");
+ } else if (strlen(vnc_connect_str) > 38) {
+ char trim[100];
+ trim[0] = '\0';
+ strncat(trim, vnc_connect_str, 38);
+ rfbLog("read VNC_CONNECT: %s ...\n", trim);
+
+ } else {
+ rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str);
+ }
+}
+
+/*
+ * check if client_connect has been set, if so make the reverse connections.
+ */
+static void send_client_connect(void) {
+ if (client_connect != NULL) {
+ char *str = client_connect;
+ if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) {
+ process_remote_cmd(client_connect, 0);
+ } else if (strstr(str, "ans=") == str
+ || strstr(str, "aro=") == str) {
+ ;
+ } else if (strstr(str, "ack=") == str) {
+ ;
+ } else {
+ reverse_connect(client_connect);
+ }
+ free(client_connect);
+ client_connect = NULL;
+ }
+}
+
+/*
+ * monitor the various input methods
+ */
+void check_connect_inputs(void) {
+
+ /* flush any already set: */
+ send_client_connect();
+
+ /* connect file: */
+ if (client_connect_file != NULL) {
+ check_connect_file(client_connect_file);
+ }
+ send_client_connect();
+
+ /* VNC_CONNECT property (vncconnect program) */
+ if (vnc_connect && *vnc_connect_str != '\0') {
+ client_connect = strdup(vnc_connect_str);
+ vnc_connect_str[0] = '\0';
+ }
+ send_client_connect();
+}
+
+void check_gui_inputs(void) {
+ int i, nmax = 0, n = 0, nfds;
+ int socks[ICON_MODE_SOCKS];
+ fd_set fds;
+ struct timeval tv;
+ char buf[VNC_CONNECT_MAX+1];
+ ssize_t nbytes;
+
+ for (i=0; i<ICON_MODE_SOCKS; i++) {
+ if (icon_mode_socks[i] >= 0) {
+ socks[n++] = i;
+ if (icon_mode_socks[i] > nmax) {
+ nmax = icon_mode_socks[i];
+ }
+ }
+ }
+
+ if (! n) {
+ return;
+ }
+
+ FD_ZERO(&fds);
+ for (i=0; i<n; i++) {
+ FD_SET(icon_mode_socks[socks[i]], &fds);
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ nfds = select(nmax+1, &fds, NULL, NULL, &tv);
+ if (nfds <= 0) {
+ return;
+ }
+
+ for (i=0; i<n; i++) {
+ int k, fd = icon_mode_socks[socks[i]];
+ char *p;
+ if (! FD_ISSET(fd, &fds)) {
+ continue;
+ }
+ for (k=0; k<=VNC_CONNECT_MAX; k++) {
+ buf[k] = '\0';
+ }
+ nbytes = read(fd, buf, VNC_CONNECT_MAX);
+ if (nbytes <= 0) {
+ close(fd);
+ icon_mode_socks[socks[i]] = -1;
+ continue;
+ }
+
+ p = strtok(buf, "\r\n");
+ while (p) {
+ if (strstr(p, "cmd=") == p ||
+ strstr(p, "qry=") == p) {
+ char *str = process_remote_cmd(p, 1);
+ if (! str) {
+ str = strdup("");
+ }
+ nbytes = write(fd, str, strlen(str));
+ write(fd, "\n", 1);
+ free(str);
+ if (nbytes < 0) {
+ close(fd);
+ icon_mode_socks[socks[i]] = -1;
+ break;
+ }
+ }
+ p = strtok(NULL, "\r\n");
+ }
+ }
+}
+
+/*
+ * libvncserver callback for when a new client connects
+ */
+enum rfbNewClientAction new_client(rfbClientPtr client) {
+ ClientData *cd;
+
+ last_event = last_input = time(0);
+
+ if (inetd) {
+ /*
+ * Set this so we exit as soon as connection closes,
+ * otherwise client_gone is only called after RFB_CLIENT_ACCEPT
+ */
+ client->clientGoneHook = client_gone;
+ }
+
+ clients_served++;
+
+ if (connect_once) {
+ if (screen->dontDisconnect && screen->neverShared) {
+ if (! shared && accepted_client) {
+ rfbLog("denying additional client: %s\n",
+ client->host);
+ return(RFB_CLIENT_REFUSE);
+ }
+ }
+ }
+ if (! check_access(client->host)) {
+ rfbLog("denying client: %s does not match %s\n", client->host,
+ allow_list ? allow_list : "(null)" );
+ return(RFB_CLIENT_REFUSE);
+ }
+ if (! accept_client(client)) {
+ rfbLog("denying client: %s local user rejected connection.\n",
+ client->host);
+ rfbLog("denying client: accept_cmd=\"%s\"\n",
+ accept_cmd ? accept_cmd : "(null)" );
+ return(RFB_CLIENT_REFUSE);
+ }
+
+ client->clientData = (void *) calloc(sizeof(ClientData), 1);
+ cd = (ClientData *) client->clientData;
+
+ cd->uid = clients_served;
+
+ cd->client_port = get_remote_port(client->sock);
+ cd->server_port = get_local_port(client->sock);
+ cd->server_ip = get_local_host(client->sock);
+ cd->hostname = ip2host(client->host);
+ cd->username = strdup("");
+
+ cd->input[0] = '-';
+ cd->login_viewonly = -1;
+ cd->login_time = time(0);
+
+ client->clientGoneHook = client_gone;
+
+ if (client_count) {
+ speeds_net_rate_measured = 0;
+ speeds_net_latency_measured = 0;
+ }
+ client_count++;
+
+ last_keyboard_input = last_pointer_input = time(0);
+
+ if (no_autorepeat && client_count == 1 && ! view_only) {
+ /*
+ * first client, turn off X server autorepeat
+ * XXX handle dynamic change of view_only and per-client.
+ */
+ autorepeat(0, 0);
+ }
+ if (use_solid_bg && client_count == 1) {
+ solid_bg(0);
+ }
+
+ if (pad_geometry) {
+ install_padded_fb(pad_geometry);
+ }
+
+ cd->timer = dnow();
+ cd->send_cmp_rate = 0.0;
+ cd->send_raw_rate = 0.0;
+ cd->latency = 0.0;
+ cd->cmp_bytes_sent = 0;
+ cd->raw_bytes_sent = 0;
+
+ accepted_client = 1;
+ last_client = time(0);
+
+ return(RFB_CLIENT_ACCEPT);
+}
+
+void start_client_info_sock(char *host_port_cookie) {
+ char *host = NULL, *cookie = NULL, *p;
+ char *str = strdup(host_port_cookie);
+ int i, port, sock, next = -1;
+ static time_t start_time[ICON_MODE_SOCKS];
+ time_t oldest = 0;
+ int db = 0;
+
+ for (i = 0; i < ICON_MODE_SOCKS; i++) {
+ if (icon_mode_socks[i] < 0) {
+ next = i;
+ break;
+ }
+ if (oldest == 0 || start_time[i] < oldest) {
+ next = i;
+ oldest = start_time[i];
+ }
+ }
+
+ p = strtok(str, ":");
+ i = 0;
+ while (p) {
+ if (i == 0) {
+ host = strdup(p);
+ } else if (i == 1) {
+ port = atoi(p);
+ } else if (i == 2) {
+ cookie = strdup(p);
+ }
+ i++;
+ p = strtok(NULL, ":");
+ }
+ free(str);
+
+ if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next);
+
+ if (host && port && cookie) {
+ if (*host == '\0') {
+ free(host);
+ host = strdup("localhost");
+ }
+ sock = rfbConnectToTcpAddr(host, port);
+ if (sock < 0) {
+ usleep(200 * 1000);
+ sock = rfbConnectToTcpAddr(host, port);
+ }
+ if (sock >= 0) {
+ char *lst = list_clients();
+ icon_mode_socks[next] = sock;
+ start_time[next] = time(0);
+ write(sock, "COOKIE:", strlen("COOKIE:"));
+ write(sock, cookie, strlen(cookie));
+ write(sock, "\n", strlen("\n"));
+ write(sock, "none\n", strlen("none\n"));
+ write(sock, "none\n", strlen("none\n"));
+ write(sock, lst, strlen(lst));
+ write(sock, "\n", strlen("\n"));
+ if (db) {
+ fprintf(stderr, "list: %s\n", lst);
+ }
+ free(lst);
+ rfbLog("client_info_sock to: %s:%d\n", host, port);
+ } else {
+ rfbLog("failed client_info_sock: %s:%d\n", host, port);
+ }
+ } else {
+ rfbLog("malformed client_info_sock: %s\n", host_port_cookie);
+ }
+
+ if (host) free(host);
+ if (cookie) free(cookie);
+}
+
+void send_client_info(char *str) {
+ int i;
+ static char *pstr = NULL;
+ static int len = 128;
+
+ if (!str || strlen(str) == 0) {
+ return;
+ }
+
+ if (!pstr) {
+ pstr = (char *)malloc(len);
+ }
+ if (strlen(str) + 2 > (size_t) len) {
+ free(pstr);
+ len *= 2;
+ pstr = (char *)malloc(len);
+ }
+ strcpy(pstr, str);
+ strcat(pstr, "\n");
+
+ if (icon_mode_fh) {
+ fprintf(icon_mode_fh, "%s", pstr);
+ fflush(icon_mode_fh);
+ }
+
+ for (i=0; i<ICON_MODE_SOCKS; i++) {
+ int len, n, sock = icon_mode_socks[i];
+ char *buf = pstr;
+
+ if (sock < 0) {
+ continue;
+ }
+
+ len = strlen(pstr);
+ while (len > 0) {
+ n = write(sock, buf, len);
+ if (n > 0) {
+ buf += n;
+ len -= n;
+ continue;
+ }
+
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
+ close(sock);
+ icon_mode_socks[i] = -1;
+ break;
+ }
+ }
+}
+
+void check_new_clients(void) {
+ static int last_count = 0;
+ rfbClientIteratorPtr iter;
+ rfbClientPtr cl;
+ int i, send_info = 0;
+ int run_after_accept = 0;
+
+ if (client_count == last_count) {
+ return;
+ }
+
+ if (! all_clients_initialized()) {
+ return;
+ }
+
+ if (client_count > last_count) {
+ if (afteraccept_cmd != NULL && afteraccept_cmd[0] != '\0') {
+ run_after_accept = 1;
+ }
+ }
+
+ last_count = client_count;
+
+ if (! screen) {
+ return;
+ }
+ if (! client_count) {
+ send_client_info("none");
+ return;
+ }
+
+ iter = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(iter)) ) {
+ ClientData *cd = (ClientData *) cl->clientData;
+
+ if (cd->login_viewonly < 0) {
+ /* this is a general trigger to initialize things */
+ if (cl->viewOnly) {
+ cd->login_viewonly = 1;
+ if (allowed_input_view_only) {
+ cl->viewOnly = FALSE;
+ cd->input[0] = '\0';
+ strncpy(cd->input,
+ allowed_input_view_only, CILEN);
+ }
+ } else {
+ cd->login_viewonly = 0;
+ if (allowed_input_normal) {
+ cd->input[0] = '\0';
+ strncpy(cd->input,
+ allowed_input_normal, CILEN);
+ }
+ }
+ if (run_after_accept) {
+ run_user_command(afteraccept_cmd, cl,
+ "afteraccept");
+ }
+ }
+ }
+ rfbReleaseClientIterator(iter);
+
+ if (icon_mode_fh) {
+ send_info++;
+ }
+ for (i = 0; i < ICON_MODE_SOCKS; i++) {
+ if (send_info || icon_mode_socks[i] >= 0) {
+ send_info++;
+ break;
+ }
+ }
+ if (send_info) {
+ char *str = list_clients();
+ send_client_info(str);
+ free(str);
+ }
+}
+
+