/* * Copyright (C) 2003 Sun Microsystems, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Authors: * Mark McLoughlin */ #include #include "vino-server.h" #include #include "vino-fb.h" #ifdef VINO_ENABLE_HTTP_SERVER #include "vino-http.h" #endif #include "vino-mdns.h" #include "vino-input.h" #include "vino-cursor.h" #include "vino-prompt.h" #include "vino-status-icon.h" #include "vino-dbus-listener.h" #include "vino-util.h" #include "vino-enums.h" #include #include #ifdef VINO_ENABLE_KEYRING #include #endif /* If an authentication attempt failes, delay the next * authentication attempt for 5 seconds. */ #define VINO_SERVER_AUTH_DEFER_LEN 5 struct _VinoServerPrivate { rfbScreenInfoPtr rfb_screen; GdkScreen *screen; VinoFB *fb; VinoCursorData *cursor_data; VinoPrompt *prompt; VinoStatusIcon *icon; VinoDBusListener *listener; GIOChannel *io_channel; guint io_watch; GSList *clients; VinoAuthMethod auth_methods; char *vnc_password; #ifdef VINO_ENABLE_HTTP_SERVER VinoHTTP *http; #endif int alternative_port; guint on_hold : 1; guint local_only : 1; guint prompt_enabled : 1; guint view_only : 1; guint require_encryption : 1; guint last_auth_failed : 1; guint use_alternative_port : 1; guint lock_screen : 1; }; struct _VinoClient { rfbClientPtr rfb_client; GIOChannel *io_channel; guint io_watch; guint update_timeout; /* Deferred authentication */ guint auth_timeout; char *auth_response; int auth_resp_len; }; enum { PROP_0, PROP_SCREEN, PROP_ON_HOLD, PROP_PROMPT_ENABLED, PROP_VIEW_ONLY, PROP_LOCAL_ONLY, PROP_USE_ALTERNATIVE_PORT, PROP_ALTERNATIVE_PORT, PROP_REQUIRE_ENCRYPTION, PROP_AUTH_METHODS, PROP_VNC_PASSWORD, PROP_PORT, PROP_LOCK_SCREEN }; static enum rfbNewClientAction vino_server_auth_client (VinoServer *server, VinoClient *client, const char *response, int length); static void vino_server_setup_framebuffer (VinoServer *server); static void vino_server_release_framebuffer (VinoServer *server); static void vino_server_update_security_types (VinoServer *server); static gpointer parent_class; static void vino_server_lock_screen (VinoServer *server) { #define GNOME_SCREENSAVER_BUS_NAME "org.gnome.ScreenSaver" #define GNOME_SCREENSAVER_INTERFACE "org.gnome.ScreenSaver" #define GNOME_SCREENSAVER_PATH "/org/gnome/ScreenSaver" DBusGConnection *connection; GError *error; DBusGProxy *proxy; if (!server->priv->lock_screen) return; dprintf(DBUS, "Locking screen via gnome-screensaver\n"); error = NULL; connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (!connection) { g_printerr (_("Failed to open connection to bus: %s\n"), error->message); g_error_free (error); return; } proxy = dbus_g_proxy_new_for_name (connection, GNOME_SCREENSAVER_BUS_NAME, GNOME_SCREENSAVER_PATH, GNOME_SCREENSAVER_INTERFACE); dbus_g_proxy_call_no_reply (proxy, "Lock", G_TYPE_INVALID); g_object_unref (proxy); dbus_g_connection_unref (connection); #undef GNOME_SCREENSAVER_BUS_NAME #undef GNOME_SCREENSAVER_INTERFACE #undef GNOME_SCREENSAVER_PATH } static void vino_server_client_accepted (VinoServer *server, VinoClient *client) { if (!server->priv->icon) server->priv->icon = vino_status_icon_new (server, server->priv->screen); vino_status_icon_add_client (server->priv->icon, client); } static void vino_server_client_disconnected (VinoServer *server, VinoClient *client) { if (server->priv->icon) if (vino_status_icon_remove_client (server->priv->icon, client)) { g_object_unref (server->priv->icon); server->priv->icon = NULL; vino_server_lock_screen (server); } } static void vino_server_handle_client_gone (rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); GSList *l; g_return_if_fail (VINO_IS_SERVER (server)); dprintf (RFB, "Client with fd == %d gone\n", rfb_client->sock); vino_prompt_remove_client (server->priv->prompt, rfb_client); for (l = server->priv->clients; l; l = l->next) { VinoClient *client = (VinoClient *) l->data; if (rfb_client == client->rfb_client) { dprintf (RFB, "Found client %p ... cleaning up\n", client->rfb_client); if (client->auth_timeout) g_source_remove (client->auth_timeout); client->auth_timeout = 0; if (client->auth_response) g_free (client->auth_response); client->auth_response = NULL; client->auth_resp_len = 0; if (client->update_timeout) g_source_remove (client->update_timeout); client->update_timeout = 0; if (client->io_watch) g_source_remove (client->io_watch); client->io_watch = 0; g_io_channel_unref (client->io_channel); client->io_channel = NULL; g_free (l->data); server->priv->clients = g_slist_delete_link (server->priv->clients, l); vino_server_client_disconnected (server, client); break; } } if (!server->priv->clients) vino_server_release_framebuffer (server); } static gboolean vino_server_update_client (rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); const char *cursor_source; const char *cursor_mask; int x, y; int width, height; g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); if (vino_cursor_get_x_source (server->priv->cursor_data, &width, &height, &cursor_source, &cursor_mask)) { rfbCursorPtr rfb_cursor; rfb_cursor = rfbMakeXCursor (width, height, cursor_source, cursor_mask); rfbSetCursor (rfb_client->screen, rfb_cursor, TRUE); } vino_cursor_get_position (server->priv->cursor_data, &x, &y); rfbSetCursorPosition (rfb_client->screen, NULL, x, y); rfbUpdateClient (rfb_client); if (rfb_client->sock == -1) { rfbClientConnectionGone (rfb_client); return FALSE; } return TRUE; } static gboolean vino_server_update_client_timeout (rfbClientPtr rfb_client) { if (rfb_client->onHold) return TRUE; vino_server_update_client (rfb_client); return TRUE; } static inline gboolean more_data_pending (int fd) { struct pollfd pollfd = { fd, POLLIN|POLLPRI, 0 }; return poll (&pollfd, 1, 0) == 1; } static gboolean vino_server_client_data_pending (GIOChannel *source, GIOCondition condition, rfbClientPtr rfb_client) { if (rfb_client->onHold) return TRUE; do { rfbProcessClientMessage (rfb_client); } while (more_data_pending (rfb_client->sock)); return vino_server_update_client (rfb_client); } static void vino_server_set_client_on_hold (VinoServer *server, VinoClient *client, gboolean on_hold) { rfbClientPtr rfb_client = client->rfb_client; dprintf (RFB, "Setting client '%s' on hold: %s\n", rfb_client->host, on_hold ? "(true)" : "(false)"); rfb_client->onHold = on_hold; if (on_hold) { if (client->io_watch) { g_source_remove (client->io_watch); client->io_watch = 0; } if (client->update_timeout) { g_source_remove (client->update_timeout); client->update_timeout = 0; } } else { if (!client->io_watch) { client->io_watch = g_io_add_watch (client->io_channel, G_IO_IN|G_IO_PRI, (GIOFunc) vino_server_client_data_pending, rfb_client); } if (!client->update_timeout) { client->update_timeout = g_timeout_add (50, (GSourceFunc) vino_server_update_client_timeout, rfb_client); } } } static enum rfbNewClientAction vino_server_handle_new_client (rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); VinoClient *client; g_return_val_if_fail (VINO_IS_SERVER (server), RFB_CLIENT_REFUSE); dprintf (RFB, "New client on fd %d\n", rfb_client->sock); if (!server->priv->fb) vino_server_setup_framebuffer (server); client = g_new0 (VinoClient, 1); rfb_client->clientData = client; client->rfb_client = rfb_client; client->rfb_client->clientGoneHook = vino_server_handle_client_gone; client->io_channel = g_io_channel_unix_new (rfb_client->sock); server->priv->clients = g_slist_prepend (server->priv->clients, client); vino_server_set_client_on_hold (server, client, server->priv->on_hold); return server->priv->on_hold ? RFB_CLIENT_ON_HOLD : RFB_CLIENT_ACCEPT; } static void vino_server_handle_prompt_response (VinoServer *server, rfbClientPtr rfb_client, VinoPromptResponse response) { VinoClient *client = (VinoClient *) rfb_client->clientData; g_return_if_fail (VINO_IS_SERVER (server)); g_return_if_fail (rfb_client != NULL); switch (response) { case VINO_RESPONSE_ACCEPT: vino_server_set_client_on_hold (server, client, FALSE); vino_server_client_accepted (server, client); break; case VINO_RESPONSE_REJECT: rfbCloseClient (rfb_client); rfbClientConnectionGone (rfb_client); break; case VINO_RESPONSE_INVALID: g_assert_not_reached (); break; } } static enum rfbNewClientAction vino_server_handle_authenticated_client (rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); VinoClient *client = (VinoClient *) rfb_client->clientData; g_return_val_if_fail (VINO_IS_SERVER (server), RFB_CLIENT_REFUSE); if (!server->priv->prompt_enabled) { vino_server_client_accepted (server, client); return RFB_CLIENT_ACCEPT; } vino_prompt_add_client (server->priv->prompt, rfb_client); vino_server_set_client_on_hold (server, client, TRUE); return RFB_CLIENT_ON_HOLD; } static gboolean vino_server_new_connection_pending (GIOChannel *source, GIOCondition condition, VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); rfbProcessNewConnection (server->priv->rfb_screen); return TRUE; } static void vino_server_handle_key_event (rfbBool down, rfbKeySym keySym, rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); g_return_if_fail (VINO_IS_SERVER (server)); if (server->priv->view_only) return; vino_input_handle_key_event (server->priv->screen, keySym, down); } static void vino_server_handle_pointer_event (int buttonMask, int x, int y, rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); g_return_if_fail (VINO_IS_SERVER (server)); if (server->priv->view_only) return; vino_input_handle_pointer_event (server->priv->screen, buttonMask, x, y); } static void vino_server_handle_clipboard_event (char *str, int len, rfbClientPtr rfb_client) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); g_return_if_fail (VINO_IS_SERVER (server)); if (server->priv->view_only) return; vino_input_handle_clipboard_event (server->priv->screen, str, len); } static gboolean vino_server_auth_client_deferred (VinoClient *client) { VinoServer *server = VINO_SERVER (client->rfb_client->screen->screenData); enum rfbNewClientAction result; result = vino_server_auth_client (server, client, client->auth_response, client->auth_resp_len); if (result == RFB_CLIENT_ACCEPT) vino_server_set_client_on_hold (server, client, FALSE); rfbAuthPasswordChecked (client->rfb_client, result); g_free (client->auth_response); client->auth_response = NULL; client->auth_resp_len = 0; client->auth_timeout = 0; return FALSE; } static void vino_server_defer_client_auth (VinoServer *server, VinoClient *client, const char *response, int length) { client->auth_resp_len = length; client->auth_response = g_new (char, length); memcpy (client->auth_response, response, length); vino_server_set_client_on_hold (server, client, TRUE); client->auth_timeout = g_timeout_add (VINO_SERVER_AUTH_DEFER_LEN * 1000, (GSourceFunc) vino_server_auth_client_deferred, client); } static char * vino_server_get_password_from_keyring (VinoServer *server) { #ifdef VINO_ENABLE_KEYRING GnomeKeyringNetworkPasswordData *found_item; GnomeKeyringResult result; GList *matches; char *password; matches = NULL; result = gnome_keyring_find_network_password_sync ( NULL, /* user */ NULL, /* domain */ "vino.local", /* server */ NULL, /* object */ "rfb", /* protocol */ "vnc-password", /* authtype */ 5900, /* port */ &matches); if (result != GNOME_KEYRING_RESULT_OK || matches == NULL || matches->data == NULL) return NULL; found_item = (GnomeKeyringNetworkPasswordData *) matches->data; password = g_strdup (found_item->password); gnome_keyring_network_password_list_free (matches); return password; #else return NULL; #endif } static enum rfbNewClientAction vino_server_auth_client (VinoServer *server, VinoClient *client, const char *response, int length) { rfbClientPtr rfb_client; char *password; if (!(server->priv->auth_methods & VINO_AUTH_VNC)) goto auth_failed; if (!(password = vino_server_get_password_from_keyring (server))) { guchar *blob; gsize blob_len; if (!server->priv->vnc_password) goto auth_failed; blob_len = 0; blob = g_base64_decode (server->priv->vnc_password, &blob_len); password = g_strndup ((char *) blob, blob_len); g_free (blob); } rfb_client = client->rfb_client; if (!password) goto auth_failed; vncEncryptBytes (client->rfb_client->authChallenge, password); memset (password, 0, strlen (password)); g_free (password); if (memcmp (rfb_client->authChallenge, response, length)) { memset (rfb_client->authChallenge, 0, CHALLENGESIZE); g_warning ("VNC authentication failure from '%s'\n", rfb_client->host); goto auth_failed; } memset (rfb_client->authChallenge, 0, CHALLENGESIZE); server->priv->last_auth_failed = FALSE; return RFB_CLIENT_ACCEPT; auth_failed: /* Delay the next authentication attempt so as to make * brute force guessing of the password infeasible. */ server->priv->last_auth_failed = TRUE; return RFB_CLIENT_REFUSE; } static enum rfbNewClientAction vino_server_check_vnc_password (rfbClientPtr rfb_client, const char *response, int length) { VinoServer *server = VINO_SERVER (rfb_client->screen->screenData); VinoClient *client = (VinoClient *) rfb_client->clientData; g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); if (!response || length != CHALLENGESIZE) { server->priv->last_auth_failed = TRUE; return RFB_CLIENT_REFUSE; } if (server->priv->last_auth_failed) { g_warning ("Deferring authentication of '%s' for %d seconds\n", rfb_client->host, VINO_SERVER_AUTH_DEFER_LEN); vino_server_defer_client_auth (server, client, response, length); return RFB_CLIENT_ON_HOLD; } return vino_server_auth_client (server, client, response, length); } static void vino_server_handle_damage_notify (VinoServer *server) { GdkRectangle *rects; int i, n_rects; g_return_if_fail (VINO_IS_SERVER (server)); rects = vino_fb_get_damage (server->priv->fb, &n_rects, TRUE); for (i = 0; i < n_rects; i++) rfbMarkRectAsModified (server->priv->rfb_screen, rects [i].x, rects [i].y, rects [i].x + rects [i].width, rects [i].y + rects [i].height); g_free (rects); } static void vino_server_init_pixel_format (VinoServer *server, rfbScreenInfoPtr rfb_screen) { rfbPixelFormat *format = &rfb_screen->rfbServerFormat; gulong red_mask, green_mask, blue_mask; rfb_screen->bitsPerPixel = vino_fb_get_bits_per_pixel (server->priv->fb); rfb_screen->depth = vino_fb_get_depth (server->priv->fb); rfb_screen->paddedWidthInBytes = vino_fb_get_rowstride (server->priv->fb); format->bitsPerPixel = rfb_screen->bitsPerPixel; format->depth = rfb_screen->depth; vino_fb_get_color_masks (server->priv->fb, &red_mask, &green_mask, &blue_mask); format->redShift = 0; while (!(red_mask & (1 << format->redShift))) format->redShift++; format->greenShift = 0; while (!(green_mask & (1 << format->greenShift))) format->greenShift++; format->blueShift = 0; while (!(blue_mask & (1 << format->blueShift))) format->blueShift++; format->redMax = red_mask >> format->redShift; format->greenMax = green_mask >> format->greenShift; format->blueMax = blue_mask >> format->blueShift; dprintf (RFB, "Initialized pixel format: %dbpp, depth = %d\n" "\tred: mask = %.8lx, max = %d, shift = %d\n" "\tgreen: mask = %.8lx, max = %d, shift = %d\n" "\tblue: mask = %.8lx, max = %d, shift = %d\n", format->bitsPerPixel, format->depth, red_mask, format->redMax, format->redShift, green_mask, format->greenMax, format->greenShift, blue_mask, format->blueMax, format->blueShift); } static void vino_server_screen_size_changed (VinoServer *server) { g_return_if_fail (VINO_IS_SERVER (server)); rfbNewFramebuffer (server->priv->rfb_screen, vino_fb_get_pixels (server->priv->fb), gdk_screen_get_width (server->priv->screen), gdk_screen_get_height (server->priv->screen)); vino_server_init_pixel_format (server, server->priv->rfb_screen); } static void vino_server_setup_framebuffer (VinoServer *server) { g_return_if_fail (server->priv->fb == NULL); g_return_if_fail (server->priv->cursor_data == NULL); server->priv->fb = vino_fb_new (server->priv->screen); g_signal_connect_swapped (server->priv->fb, "size-changed", G_CALLBACK (vino_server_screen_size_changed), server); g_signal_connect_swapped (server->priv->fb, "damage-notify", G_CALLBACK (vino_server_handle_damage_notify), server); rfbNewFramebuffer (server->priv->rfb_screen, vino_fb_get_pixels (server->priv->fb), gdk_screen_get_width (server->priv->screen), gdk_screen_get_height (server->priv->screen)); vino_server_init_pixel_format (server, server->priv->rfb_screen); server->priv->cursor_data = vino_cursor_init (server->priv->screen); } static void vino_server_release_framebuffer (VinoServer *server) { g_return_if_fail (server->priv->fb != NULL); g_return_if_fail (server->priv->cursor_data != NULL); if (server->priv->cursor_data) vino_cursor_finalize (server->priv->cursor_data); server->priv->cursor_data = NULL; server->priv->rfb_screen->frameBuffer = NULL; g_object_unref (server->priv->fb); server->priv->fb = NULL; } static void vino_server_init_from_screen (VinoServer *server, GdkScreen *screen) { rfbScreenInfoPtr rfb_screen; char *name; g_return_if_fail (server->priv->screen == NULL); g_return_if_fail (screen != NULL); server->priv->screen = screen; server->priv->prompt = vino_prompt_new (screen); g_signal_connect_swapped (server->priv->prompt, "response", G_CALLBACK (vino_server_handle_prompt_response), server); /* libvncserver NOTE: * we don't pass in argc or argv * samplesPerPixel is totally unused (3 below) * bitsPerSample and bytesPerPixel get set in * init_pixel_format() */ server->priv->rfb_screen = rfb_screen = rfbGetScreen (NULL, NULL, gdk_screen_get_width (screen), gdk_screen_get_height (screen), -1, -1, -1); name = g_strjoin ("@", g_get_user_name (), g_get_host_name (), NULL); rfbSetDesktopName(rfb_screen, name); g_free (name); /* libvncserver NOTE: * DeferUpdateTime is the number of milliseconds to wait * before actually responding to a frame buffer update * request. * setting autoPort enables autoProbing a port between * 5900-6000 */ rfb_screen->rfbDeferUpdateTime = 0; rfb_screen->localOnly = server->priv->local_only; rfb_screen->autoPort = TRUE; rfb_screen->rfbPort = VINO_SERVER_DEFAULT_PORT; rfb_screen->rfbAlwaysShared = TRUE; if (server->priv->use_alternative_port) { rfb_screen->autoPort = FALSE; rfb_screen->rfbPort = server->priv->alternative_port; } rfbInitServer (rfb_screen); /* libvncserver NOTE: * there's no user_data for newClientHook. You have * to use screenData */ rfb_screen->screenData = server; rfb_screen->newClientHook = vino_server_handle_new_client; rfb_screen->authenticatedClientHook = vino_server_handle_authenticated_client; /* libvncserver NOTE: * all these hooks should take an rfbClientPtr as the * first argument rather than the last. */ rfb_screen->kbdAddEvent = vino_server_handle_key_event; rfb_screen->ptrAddEvent = vino_server_handle_pointer_event; rfb_screen->setXCutText = vino_server_handle_clipboard_event; rfb_screen->passwordCheck = vino_server_check_vnc_password; vino_server_update_security_types (server); dprintf (RFB, "Creating watch for listening socket %d - port %d\n", rfb_screen->rfbListenSock, rfb_screen->rfbPort); server->priv->io_channel = g_io_channel_unix_new (rfb_screen->rfbListenSock); server->priv->io_watch = g_io_add_watch (server->priv->io_channel, G_IO_IN|G_IO_PRI, (GIOFunc) vino_server_new_connection_pending, server); #ifdef VINO_ENABLE_HTTP_SERVER server->priv->http = vino_http_get (rfb_screen->rfbPort); #endif vino_mdns_add_service ("_rfb._tcp", rfb_screen->rfbPort); server->priv->listener = vino_dbus_listener_new (server); } static void vino_server_finalize (GObject *object) { VinoServer *server = VINO_SERVER (object); #ifdef VINO_ENABLE_HTTP_SERVER if (server->priv->http) { vino_http_remove_rfb_port (server->priv->http, server->priv->rfb_screen->rfbPort); g_object_unref (server->priv->http); } server->priv->http = NULL; #endif /* VINO_ENABLE_HTTP_SERVER */ if (server->priv->io_watch) g_source_remove (server->priv->io_watch); server->priv->io_watch = 0; if (server->priv->io_channel) g_io_channel_unref (server->priv->io_channel); server->priv->io_channel = NULL; if (server->priv->rfb_screen) rfbScreenCleanup (server->priv->rfb_screen); server->priv->rfb_screen = NULL; /* ClientGoneHook should get invoked for each * client from rfbScreenCleanup() */ g_assert (server->priv->clients == NULL); if (server->priv->vnc_password) g_free (server->priv->vnc_password); server->priv->vnc_password = NULL; if (server->priv->prompt) g_object_unref (server->priv->prompt); server->priv->prompt = NULL; if (server->priv->cursor_data) vino_cursor_finalize (server->priv->cursor_data); server->priv->cursor_data = NULL; if (server->priv->fb) g_object_unref (server->priv->fb); server->priv->fb = NULL; if (server->priv->listener) g_object_unref (server->priv->listener); server->priv->listener = NULL; g_free (server->priv); server->priv = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); } static void vino_server_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { VinoServer *server = VINO_SERVER (object); switch (prop_id) { case PROP_SCREEN: vino_server_init_from_screen (server, g_value_get_object (value)); break; case PROP_ON_HOLD: vino_server_set_on_hold (server, g_value_get_boolean (value)); break; case PROP_PROMPT_ENABLED: vino_server_set_prompt_enabled (server, g_value_get_boolean (value)); break; case PROP_VIEW_ONLY: vino_server_set_view_only (server, g_value_get_boolean (value)); break; case PROP_LOCAL_ONLY: vino_server_set_local_only (server, g_value_get_boolean (value)); break; case PROP_REQUIRE_ENCRYPTION: vino_server_set_require_encryption (server, g_value_get_boolean (value)); break; case PROP_AUTH_METHODS: vino_server_set_auth_methods (server, g_value_get_flags (value)); break; case PROP_VNC_PASSWORD: vino_server_set_vnc_password (server, g_value_get_string (value)); break; case PROP_USE_ALTERNATIVE_PORT: vino_server_set_use_alternative_port (server, g_value_get_boolean (value)); break; case PROP_ALTERNATIVE_PORT: vino_server_set_alternative_port (server, g_value_get_int (value)); break; case PROP_LOCK_SCREEN: vino_server_set_lock_screen (server, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void vino_server_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { VinoServer *server = VINO_SERVER (object); switch (prop_id) { case PROP_SCREEN: g_value_set_object (value, server->priv->screen); break; case PROP_ON_HOLD: g_value_set_boolean (value, server->priv->on_hold); break; case PROP_PROMPT_ENABLED: g_value_set_boolean (value, server->priv->prompt_enabled); break; case PROP_VIEW_ONLY: g_value_set_boolean (value, server->priv->view_only); break; case PROP_LOCAL_ONLY: g_value_set_boolean (value, server->priv->local_only); break; case PROP_REQUIRE_ENCRYPTION: g_value_set_boolean (value, server->priv->require_encryption); break; case PROP_AUTH_METHODS: g_value_set_flags (value, server->priv->auth_methods); break; case PROP_VNC_PASSWORD: g_value_set_string (value, server->priv->vnc_password); break; case PROP_USE_ALTERNATIVE_PORT: g_value_set_boolean (value, server->priv->use_alternative_port); break; case PROP_ALTERNATIVE_PORT: g_value_set_int (value, server->priv->alternative_port); break; case PROP_PORT: g_value_set_int (value, server->priv->rfb_screen->rfbPort); break; case PROP_LOCK_SCREEN: g_value_set_boolean (value, server->priv->lock_screen); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void vino_server_instance_init (VinoServer *server) { server->priv = g_new0 (VinoServerPrivate, 1); } static void vino_server_class_init (VinoServerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = vino_server_finalize; gobject_class->set_property = vino_server_set_property; gobject_class->get_property = vino_server_get_property; g_object_class_install_property (gobject_class, PROP_SCREEN, g_param_spec_object ("screen", "Screen", "The screen for which to create a VNC server", GDK_TYPE_SCREEN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_ON_HOLD, g_param_spec_boolean ("on-hold", "On Hold", "Place all clients on hold", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_PROMPT_ENABLED, g_param_spec_boolean ("prompt-enabled", "Prompt enabled", "Prompt the user about connection attempts", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_VIEW_ONLY, g_param_spec_boolean ("view-only", "View Only", "Disallow keyboard/pointer input from clients", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_LOCAL_ONLY, g_param_spec_boolean ("local-only", "Local Only", "Only allow local connections", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_REQUIRE_ENCRYPTION, g_param_spec_boolean ("require-encryption", "Require Encryption", "Require clients to use encryption", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_AUTH_METHODS, g_param_spec_flags ("auth-methods", "Authentication methods", "The authentication methods this server should allow", VINO_TYPE_AUTH_METHOD, VINO_AUTH_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_VNC_PASSWORD, g_param_spec_string ("vnc-password", "VNC Password", "The password (base64 encoded) used to authenticate types using the VncAuth method", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_USE_ALTERNATIVE_PORT, g_param_spec_boolean ("use-alternative-port", "Use an alternative port", "Listen on the port specified by the 'alternative-port' property", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_ALTERNATIVE_PORT, g_param_spec_int ("alternative-port", "Alternative port number", "Listen on the specified port number if the 'use-alternative-port' property is TRUE", VINO_SERVER_MIN_PORT, VINO_SERVER_MAX_PORT, VINO_SERVER_DEFAULT_PORT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_PORT, g_param_spec_int ("port", "Server port number", "The port used by this server", VINO_SERVER_MIN_PORT, VINO_SERVER_MAX_PORT, VINO_SERVER_DEFAULT_PORT, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (gobject_class, PROP_LOCK_SCREEN, g_param_spec_boolean ("lock-screen", "Locks screen on disconnect", "After last user disconnects, screen will be locked", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } GType vino_server_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (VinoServerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) vino_server_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (VinoServer), 0, /* n_preallocs */ (GInstanceInitFunc) vino_server_instance_init, }; object_type = g_type_register_static (G_TYPE_OBJECT, "VinoServer", &object_info, 0); } return object_type; } VinoServer * vino_server_new (GdkScreen *screen, gboolean view_only) { g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); return g_object_new (VINO_TYPE_SERVER, "screen", screen, "view-only", view_only, NULL); } GdkScreen * vino_server_get_screen (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), NULL); return server->priv->screen; } gboolean vino_server_get_view_only (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->view_only; } void vino_server_set_view_only (VinoServer *server, gboolean view_only) { g_return_if_fail (VINO_IS_SERVER (server)); view_only = view_only != FALSE; if (server->priv->view_only != view_only) { server->priv->view_only = view_only; g_object_notify (G_OBJECT (server), "view-only"); } } gboolean vino_server_get_local_only (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->local_only; } void vino_server_set_local_only (VinoServer *server, gboolean local_only) { g_return_if_fail (VINO_IS_SERVER (server)); local_only = local_only != FALSE; if (server->priv->local_only != local_only) { server->priv->local_only = local_only; if (server->priv->rfb_screen != NULL) rfbSetLocalOnly (server->priv->rfb_screen, server->priv->local_only); g_object_notify (G_OBJECT (server), "local-only"); } } gboolean vino_server_get_use_alternative_port (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->use_alternative_port; } void vino_server_set_use_alternative_port (VinoServer *server, gboolean use_alternative_port) { g_return_if_fail (VINO_IS_SERVER (server)); use_alternative_port = use_alternative_port != FALSE; if (server->priv->use_alternative_port != use_alternative_port) { server->priv->use_alternative_port = use_alternative_port; if (server->priv->rfb_screen) { if (server->priv->use_alternative_port) rfbSetPort (server->priv->rfb_screen, server->priv->alternative_port); rfbSetAutoPort (server->priv->rfb_screen, !server->priv->use_alternative_port); } g_object_notify (G_OBJECT (server), "use-alternative-port"); } } gint vino_server_get_alternative_port (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->alternative_port; } void vino_server_set_alternative_port (VinoServer *server, int alternative_port) { g_return_if_fail (VINO_IS_SERVER (server)); if (server->priv->alternative_port != alternative_port) { server->priv->alternative_port = alternative_port; if (server->priv->rfb_screen && server->priv->use_alternative_port) rfbSetPort (server->priv->rfb_screen, server->priv->alternative_port); g_object_notify (G_OBJECT (server), "alternative-port"); } } gboolean vino_server_get_on_hold (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->on_hold; } void vino_server_set_on_hold (VinoServer *server, gboolean on_hold) { g_return_if_fail (VINO_IS_SERVER (server)); on_hold = on_hold != FALSE; if (server->priv->on_hold != on_hold) { GSList *l; server->priv->on_hold = on_hold; for (l = server->priv->clients; l; l = l->next) { VinoClient *client = l->data; /* If a client is on hold before we have initialized, * we want to leave it on hold until the deferred * authentication or the prompt has completed. * * FIXME: this isn't correct, though - e.g if we're * in the middle of prompting and you toggle the * server on and off then the client gets through. */ if (client->rfb_client->state == RFB_NORMAL) vino_server_set_client_on_hold (server, client, on_hold); } g_object_notify (G_OBJECT (server), "on-hold"); } } gboolean vino_server_get_prompt_enabled (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->prompt_enabled; } void vino_server_set_prompt_enabled (VinoServer *server, gboolean prompt_enabled) { g_return_if_fail (VINO_IS_SERVER (server)); prompt_enabled = prompt_enabled != FALSE; if (server->priv->prompt_enabled != prompt_enabled) { server->priv->prompt_enabled = prompt_enabled; g_object_notify (G_OBJECT (server), "prompt-enabled"); } } static void vino_server_update_security_types (VinoServer *server) { if (!server->priv->rfb_screen) return; rfbClearSecurityTypes (server->priv->rfb_screen); rfbClearAuthTypes (server->priv->rfb_screen); #ifdef HAVE_GNUTLS rfbAddSecurityType (server->priv->rfb_screen, rfbTLS); #endif if (server->priv->auth_methods & VINO_AUTH_VNC) { rfbAddAuthType (server->priv->rfb_screen, rfbVncAuth); #ifdef HAVE_GNUTLS if (!server->priv->require_encryption) #endif rfbAddSecurityType (server->priv->rfb_screen, rfbVncAuth); } if (server->priv->auth_methods & VINO_AUTH_NONE) { rfbAddAuthType (server->priv->rfb_screen, rfbNoAuth); #ifdef HAVE_GNUTLS if (!server->priv->require_encryption) #endif rfbAddSecurityType (server->priv->rfb_screen, rfbNoAuth); } } void vino_server_set_require_encryption (VinoServer *server, gboolean require_encryption) { g_return_if_fail (VINO_IS_SERVER (server)); require_encryption = require_encryption != FALSE; if (server->priv->require_encryption != require_encryption) { server->priv->require_encryption = require_encryption; vino_server_update_security_types (server); g_object_notify (G_OBJECT (server), "require-encryption"); } } gboolean vino_server_get_require_encryption (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->require_encryption; } void vino_server_set_auth_methods (VinoServer *server, VinoAuthMethod auth_methods) { g_return_if_fail (VINO_IS_SERVER (server)); g_return_if_fail (auth_methods != VINO_AUTH_INVALID); if (server->priv->auth_methods != auth_methods) { server->priv->auth_methods = auth_methods; vino_server_update_security_types (server); g_object_notify (G_OBJECT (server), "auth-methods"); } } VinoAuthMethod vino_server_get_auth_methods (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), VINO_AUTH_INVALID); return server->priv->auth_methods; } void vino_server_set_vnc_password (VinoServer *server, const char *vnc_password) { g_return_if_fail (VINO_IS_SERVER (server)); if (server->priv->vnc_password) g_free (server->priv->vnc_password); server->priv->vnc_password = g_strdup (vnc_password); g_object_notify (G_OBJECT (server), "vnc-password"); } G_CONST_RETURN char * vino_server_get_vnc_password (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), NULL); return server->priv->vnc_password; } G_CONST_RETURN char * vino_client_get_hostname (VinoClient *client) { return client->rfb_client->host; } void vino_client_disconnect (VinoClient *client) { rfbCloseClient (client->rfb_client); rfbClientConnectionGone (client->rfb_client); } int vino_server_get_port (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), 0); return server->priv->rfb_screen->rfbPort; } gboolean vino_server_get_lock_screen (VinoServer *server) { g_return_val_if_fail (VINO_IS_SERVER (server), FALSE); return server->priv->lock_screen; } void vino_server_set_lock_screen (VinoServer *server, gboolean lock_screen) { g_return_if_fail (VINO_IS_SERVER (server)); lock_screen = lock_screen != FALSE; if (server->priv->lock_screen != lock_screen) { server->priv->lock_screen = lock_screen; g_object_notify (G_OBJECT (server), "lock-screen"); } }