/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is libguac-client-vnc. * * The Initial Developer of the Original Code is * Michael Jumper. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #include #include #include #include #include #include #include #include /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", "port", "read-only", "password", NULL }; static char* __GUAC_CLIENT = "GUAC_CLIENT"; typedef struct vnc_guac_client_data { rfbClient* rfb_client; png_byte** png_buffer; png_byte** png_buffer_alpha; int copy_rect_used; char* password; } vnc_guac_client_data; void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) { int dx, dy; guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT); GUACIO* io = gc->io; png_byte** png_buffer = ((vnc_guac_client_data*) gc->data)->png_buffer_alpha; png_byte* row; png_byte** png_row_current = png_buffer; unsigned int bytesPerRow = bpp * w; unsigned char* fb_row_current = client->rcSource; unsigned char* fb_mask = client->rcMask; unsigned char* fb_row; unsigned int v; /* Copy image data from VNC client to PNG */ for (dy = 0; dy> client->format.redShift) * 256 / (client->format.redMax+1); *(row++) = (v >> client->format.greenShift) * 256 / (client->format.greenMax+1); *(row++) = (v >> client->format.blueShift) * 256 / (client->format.blueMax+1); /* Handle mask */ if (*(fb_mask++)) *(row++) = 255; else *(row++) = 0; fb_row += bpp; } } /* SEND CURSOR */ guac_send_cursor(io, x, y, png_buffer, w, h); } void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { int dx, dy; guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT); GUACIO* io = gc->io; png_byte** png_buffer = ((vnc_guac_client_data*) gc->data)->png_buffer; png_byte* row; png_byte** png_row_current = png_buffer; unsigned int bpp = client->format.bitsPerPixel/8; unsigned int bytesPerRow = bpp * client->width; unsigned char* fb_row_current = client->frameBuffer + (y * bytesPerRow) + (x * bpp); unsigned char* fb_row; unsigned int v; /* Ignore extra update if already handled by copyrect */ if (((vnc_guac_client_data*) gc->data)->copy_rect_used) { ((vnc_guac_client_data*) gc->data)->copy_rect_used = 0; return; } /* Copy image data from VNC client to PNG */ for (dy = y; dy> client->format.redShift) * 256 / (client->format.redMax+1); *(row++) = (v >> client->format.greenShift) * 256 / (client->format.greenMax+1); *(row++) = (v >> client->format.blueShift) * 256 / (client->format.blueMax+1); fb_row += bpp; } } /* For now, only use layer 0 */ guac_send_png(io, 0, x, y, png_buffer, w, h); } void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT); GUACIO* io = gc->io; /* For now, only use layer 0 */ guac_send_copy(io, 0, src_x, src_y, w, h, 0, dest_x, dest_y); ((vnc_guac_client_data*) gc->data)->copy_rect_used = 1; } char* guac_vnc_get_password(rfbClient* client) { guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT); return ((vnc_guac_client_data*) gc->data)->password; } void guac_vnc_cut_text(rfbClient* client, const char* text, int textlen) { guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT); GUACIO* io = gc->io; guac_send_clipboard(io, text); } int vnc_guac_client_handle_messages(guac_client* client) { int wait_result; rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client; wait_result = WaitForMessage(rfb_client, 2000); if (wait_result < 0) { GUAC_LOG_ERROR("Error waiting for VNC server message\n"); return 1; } if (wait_result > 0) { if (!HandleRFBServerMessage(rfb_client)) { GUAC_LOG_ERROR("Error handling VNC server message\n"); return 1; } } return 0; } int vnc_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client; SendPointerEvent(rfb_client, x, y, mask); return 0; } int vnc_guac_client_key_handler(guac_client* client, int keysym, int pressed) { rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client; SendKeyEvent(rfb_client, keysym, pressed); return 0; } int vnc_guac_client_clipboard_handler(guac_client* client, char* data) { rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client; SendClientCutText(rfb_client, data, strlen(data)); return 0; } int vnc_guac_client_free_handler(guac_client* client) { rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client; png_byte** png_buffer = ((vnc_guac_client_data*) client->data)->png_buffer; png_byte** png_buffer_alpha = ((vnc_guac_client_data*) client->data)->png_buffer_alpha; /* Free PNG data */ guac_free_png_buffer(png_buffer, rfb_client->height); guac_free_png_buffer(png_buffer_alpha, rfb_client->height); /* Free generic data struct */ free(client->data); /* Clean up VNC client*/ rfbClientCleanup(rfb_client); return 0; } int guac_client_init(guac_client* client, int argc, char** argv) { rfbClient* rfb_client; png_byte** png_buffer; png_byte** png_buffer_alpha; vnc_guac_client_data* guac_client_data; int read_only = 0; /*** PARSE ARGUMENTS ***/ if (argc < 2) { guac_send_error(client->io, "VNC client requires hostname and port arguments"); guac_flush(client->io); return 1; } /* Alloc client data */ guac_client_data = malloc(sizeof(vnc_guac_client_data)); client->data = guac_client_data; /* If read-only specified, set flag */ if (argc >= 3) { if (strcmp(argv[2], "true") == 0) read_only = 1; } /* Parse password from args if provided */ if (argc >= 4) { /* Freed after use by libvncclient */ guac_client_data->password = malloc(64); strncpy(guac_client_data->password, argv[3], 63); } else { /* Freed after use by libvncclient */ guac_client_data->password = malloc(64); guac_client_data->password[0] = '\0'; } /*** INIT RFB CLIENT ***/ rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */ /* Framebuffer update handler */ rfb_client->GotFrameBufferUpdate = guac_vnc_update; rfb_client->GotCopyRect = guac_vnc_copyrect; /* Do not handle clipboard and local cursor if read-only */ if (read_only == 0) { /* Enable client-side cursor */ rfb_client->GotCursorShape = guac_vnc_cursor; rfb_client->appData.useRemoteCursor = TRUE; /* Clipboard */ rfb_client->GotXCutText = guac_vnc_cut_text; } /* Password */ rfb_client->GetPassword = guac_vnc_get_password; /* Store Guac client in rfb client */ rfbClientSetClientData(rfb_client, __GUAC_CLIENT, client); /* Set hostname and port */ rfb_client->serverHost = strdup(argv[0]); rfb_client->serverPort = atoi(argv[1]); /* Connect */ if (!rfbInitClient(rfb_client, NULL, NULL)) { guac_send_error(client->io, "Error initializing VNC client"); guac_flush(client->io); return 1; } /* Allocate buffers */ png_buffer = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 3); /* No-alpha */ png_buffer_alpha = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 4); /* With alpha */ /* Set remaining client data */ guac_client_data->rfb_client = rfb_client; guac_client_data->png_buffer = png_buffer; guac_client_data->png_buffer_alpha = png_buffer_alpha; guac_client_data->copy_rect_used = 0; /* Set handlers */ client->handle_messages = vnc_guac_client_handle_messages; if (read_only == 0) { /* Do not handle mouse/keyboard/clipboard if read-only */ client->mouse_handler = vnc_guac_client_mouse_handler; client->key_handler = vnc_guac_client_key_handler; client->clipboard_handler = vnc_guac_client_clipboard_handler; } /* Send name */ guac_send_name(client->io, rfb_client->desktopName); /* Send size */ guac_send_size(client->io, rfb_client->width, rfb_client->height); return 0; }