lib_LTLIBRARIES = libguac.la
-libguac_la_SOURCES = client.c clientreg.c guacio.c protocol.c uuidtree.c
+libguac_la_SOURCES = src/client.c src/clientreg.c src/guacio.c src/protocol.c src/uuidtree.c
include_HEADERS = include/client.h include/guacio.h include/protocol.h include/uuidtree.h
libguac_la_LDFLAGS = -version-info 0:0:0
+++ /dev/null
-
-/*
- * Guacamole - Clientless Remote Desktop
- * Copyright (C) 2010 Michael Jumper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <uuid/uuid.h>
-
-#include <syslog.h>
-
-#include "guacio.h"
-#include "protocol.h"
-#include "client.h"
-
-
-png_byte** guac_alloc_png_buffer(int w, int h, int bpp) {
-
- png_byte** png_buffer;
- png_byte* row;
- int y;
-
- /* Allocate rows for PNG */
- png_buffer = (png_byte**) malloc(h * sizeof(png_byte*));
- for (y=0; y<h; y++) {
- row = (png_byte*) malloc(sizeof(png_byte) * bpp * w);
- png_buffer[y] = row;
- }
-
- return png_buffer;
-}
-
-void guac_free_png_buffer(png_byte** png_buffer, int h) {
-
- int y;
-
- /* Free PNG data */
- for (y = 0; y<h; y++)
- free(png_buffer[y]);
- free(png_buffer);
-
-}
-
-guac_client* __guac_alloc_client(GUACIO* io) {
-
- /* Allocate new client (not handoff) */
- guac_client* client = malloc(sizeof(guac_client));
- memset(client, 0, sizeof(guac_client));
-
- /* Init new client */
- client->io = io;
- uuid_generate(client->uuid);
-
- return client;
-}
-
-
-guac_client* guac_get_client(int client_fd, guac_client_registry* registry, guac_client_init_handler* client_init, int argc, char** argv) {
-
- guac_client* client;
- GUACIO* io = guac_open(client_fd);
- guac_instruction instruction;
-
- /* Make copies of arguments */
- char** safe_argv = malloc(argc * sizeof(char*));
- char** scratch_argv = malloc(argc * sizeof(char*));
-
- int i;
- for (i=0; i<argc; i++)
- scratch_argv[i] = safe_argv[i] = strdup(argv[i]);
-
- /* Wait for handshaking messages */
- for (;;) {
-
- int retval;
- retval = guac_read_instruction(io, &instruction); /* 0 if no instructions finished yet, <0 if error or EOF */
-
- if (retval > 0) {
-
- /* connect -> create new client connection */
- if (strcmp(instruction.opcode, "connect") == 0) {
-
- /* Create new client */
- client = __guac_alloc_client(io);
-
- /* Register client */
- if (registry) {
- guac_register_client(registry, client);
-
- /* Send UUID to web-client */
- guac_send_uuid(io, client->uuid);
- guac_flush(client->io);
- }
-
- if (client_init(client, argc, scratch_argv) != 0)
- return NULL;
-
- break;
- }
-
- /* TODO: implement handoff */
-
- }
-
- if (retval < 0)
- return NULL; /* EOF or error */
-
- /* Otherwise, retval == 0 implies unfinished instruction */
-
- }
-
- /* Free memory used for arg copy */
- for (i=0; i<argc; i++)
- free(safe_argv[i]);
-
- free(safe_argv);
- free(scratch_argv);
-
- return client;
-
-}
-
-
-void guac_free_client(guac_client* client, guac_client_registry* registry) {
-
- if (client->free_handler) {
- if (client->free_handler(client))
- syslog(LOG_ERR, "Error calling client free handler");
- }
-
- guac_close(client->io);
-
- guac_remove_client(registry, client->uuid);
-
- free(client);
-}
-
-
-void guac_start_client(guac_client* client) {
-
- guac_instruction instruction;
- GUACIO* io = client->io;
- int wait_result;
-
- /* VNC Client Loop */
- for (;;) {
-
- /* Handle server messages */
- if (client->handle_messages) {
-
- int retval = client->handle_messages(client);
- if (retval) {
- syslog(LOG_ERR, "Error handling server messages");
- return;
- }
-
- guac_flush(client->io);
- }
-
- wait_result = guac_instructions_waiting(io);
- if (wait_result > 0) {
-
- int retval;
- retval = guac_read_instruction(io, &instruction); /* 0 if no instructions finished yet, <0 if error or EOF */
-
- if (retval > 0) {
-
- do {
-
- if (strcmp(instruction.opcode, "mouse") == 0) {
- if (client->mouse_handler)
- if (
- client->mouse_handler(
- client,
- atoi(instruction.argv[0]), /* x */
- atoi(instruction.argv[1]), /* y */
- atoi(instruction.argv[2]) /* mask */
- )
- ) {
-
- syslog(LOG_ERR, "Error handling mouse instruction");
- return;
-
- }
- }
-
- else if (strcmp(instruction.opcode, "key") == 0) {
- if (client->key_handler)
- if (
- client->key_handler(
- client,
- atoi(instruction.argv[0]), /* keysym */
- atoi(instruction.argv[1]) /* pressed */
- )
- ) {
-
- syslog(LOG_ERR, "Error handling key instruction");
- return;
-
- }
- }
-
- else if (strcmp(instruction.opcode, "clipboard") == 0) {
- if (client->clipboard_handler)
- if (
- client->clipboard_handler(
- client,
- guac_unescape_string_inplace(instruction.argv[0]) /* data */
- )
- ) {
-
- syslog(LOG_ERR, "Error handling clipboard instruction");
- return;
-
- }
- }
-
- else if (strcmp(instruction.opcode, "disconnect") == 0) {
- syslog(LOG_INFO, "Client requested disconnect");
- return;
- }
-
- } while ((retval = guac_read_instruction(io, &instruction)) > 0);
-
- if (retval < 0) {
- syslog(LOG_ERR, "Error reading instruction from stream");
- return;
- }
- }
-
- if (retval < 0) {
- syslog(LOG_ERR, "Error or end of stream");
- return; /* EOF or error */
- }
-
- /* Otherwise, retval == 0 implies unfinished instruction */
-
- }
- else if (wait_result < 0) {
- syslog(LOG_ERR, "Error waiting for next instruction");
- return;
- }
-
- }
-
-}
-
+++ /dev/null
-
-/*
- * Guacamole - Clientless Remote Desktop
- * Copyright (C) 2010 Michael Jumper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _CLIENTREG_H
-#define _CLIENTREG_H
-
-#include <stdlib.h>
-#include <uuid/uuid.h>
-
-#include "uuidtree.h"
-#include "client.h"
-
-guac_client_registry* guac_create_client_registry() {
-
- guac_client_registry* registry = malloc(sizeof(guac_client_registry));
-
- registry->root = guac_create_uuid_tree();
-
- return registry;
-
-}
-
-void guac_register_client(guac_client_registry* registry, guac_client* client) {
- guac_uuid_tree_put(registry->root, client->uuid, client);
-}
-
-guac_client* guac_find_client(guac_client_registry* registry, uuid_t uuid) {
- return (guac_client*) guac_uuid_tree_get(registry->root, uuid);
-}
-
-void guac_remove_client(guac_client_registry* registry, uuid_t uuid) {
- guac_uuid_tree_remove(registry->root, uuid);
-}
-
-void guac_cleanup_registry(guac_client_registry* registry) {
- guac_cleanup_uuid_tree(registry->root);
-}
-
-#endif
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-AC_INIT(client.c)
-AM_INIT_AUTOMAKE(libguac, 0.0.1)
+AC_INIT(src/client.c)
+AM_INIT_AUTOMAKE(libguac, 0.0.0)
# Checks for programs.
AC_PROG_CC
+++ /dev/null
-
-/*
- * Guacamole - Clientless Remote Desktop
- * Copyright (C) 2010 Michael Jumper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <time.h>
-#include <sys/select.h>
-#include <sys/time.h>
-
-#include "guacio.h"
-
-char __GUACIO_BAS64_CHARACTERS[64] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
- 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
-};
-
-GUACIO* guac_open(int fd) {
-
- GUACIO* io = malloc(sizeof(GUACIO));
- io->ready = 0;
- io->written = 0;
- io->fd = fd;
-
- /* Allocate instruction buffer */
- io->instructionbuf_size = 1024;
- io->instructionbuf = malloc(io->instructionbuf_size);
- io->instructionbuf_used_length = 0;
-
- /* Set limit */
- io->transfer_limit = 0;
-
- return io;
-
-}
-
-void guac_close(GUACIO* io) {
- guac_flush(io);
- free(io);
-}
-
-
-/* Write bytes, limit rate */
-ssize_t __guac_write(GUACIO* io, const char* buf, int count) {
-
- struct timeval start, end;
- int retval;
-
- /* Write and time how long the write takes (microseconds) */
- gettimeofday(&start, NULL);
- retval = write(io->fd, buf, count);
- gettimeofday(&end, NULL);
-
- if (retval < 0)
- return retval;
-
- if (io->transfer_limit > 0) {
-
- suseconds_t elapsed;
- suseconds_t required_usecs;
-
- /* Get elapsed time */
- elapsed = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec;
-
- /* Calculate how much time we must sleep */
- required_usecs = retval * 1000 / io->transfer_limit - elapsed; /* useconds at transfer_limit KB/s*/
-
- /* Sleep as necessary */
- if (required_usecs > 0) {
-
- struct timespec required_sleep;
-
- required_sleep.tv_sec = required_usecs / 1000000;
- required_sleep.tv_nsec = (required_usecs % 1000000) * 1000;
-
- nanosleep(&required_sleep, NULL);
-
- }
-
- }
-
- return retval;
-}
-
-ssize_t guac_write_int(GUACIO* io, unsigned int i) {
-
- char buffer[128];
- char* ptr = &(buffer[127]);
-
- *ptr = 0;
-
- do {
-
- ptr--;
- *ptr = '0' + (i % 10);
-
- i /= 10;
-
- } while (i > 0 && ptr >= buffer);
-
- return guac_write_string(io, ptr);
-
-}
-
-ssize_t guac_write_string(GUACIO* io, const char* str) {
-
- char* out_buf = io->out_buf;
-
- int retval;
-
- for (; *str != '\0'; str++) {
-
- out_buf[io->written++] = *str;
-
- /* Flush when necessary, return on error */
- if (io->written > 8188 /* sizeof(out_buf) - 4 */) {
-
- struct timeval start, end;
- suseconds_t elapsed;
-
- gettimeofday(&start, NULL);
- retval = __guac_write(io, out_buf, io->written);
- gettimeofday(&end, NULL);
-
- if (retval < 0)
- return retval;
-
- /* Get elapsed time */
- elapsed = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec;
-
- io->written = 0;
- }
-
- }
-
- return 0;
-
-}
-
-ssize_t __guac_write_base64_triplet(GUACIO* io, int a, int b, int c) {
-
- char* out_buf = io->out_buf;
-
- int retval;
-
- /* Byte 1 */
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[(a & 0xFC) >> 2]; /* [AAAAAA]AABBBB BBBBCC CCCCCC */
-
- if (b >= 0) {
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((a & 0x03) << 4) | ((b & 0xF0) >> 4)]; /* AAAAAA[AABBBB]BBBBCC CCCCCC */
-
- if (c >= 0) {
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)]; /* AAAAAA AABBBB[BBBBCC]CCCCCC */
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[c & 0x3F]; /* AAAAAA AABBBB BBBBCC[CCCCCC] */
- }
- else {
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((b & 0x0F) << 2)]; /* AAAAAA AABBBB[BBBB--]------ */
- out_buf[io->written++] = '='; /* AAAAAA AABBBB BBBB--[------] */
- }
- }
- else {
- out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((a & 0x03) << 4)]; /* AAAAAA[AA----]------ ------ */
- out_buf[io->written++] = '='; /* AAAAAA AA----[------]------ */
- out_buf[io->written++] = '='; /* AAAAAA AA---- ------[------] */
- }
-
- /* At this point, 4 bytes have been io->written */
-
- /* Flush when necessary, return on error */
- if (io->written > 8188 /* sizeof(out_buf) - 4 */) {
- retval = __guac_write(io, out_buf, io->written);
- if (retval < 0)
- return retval;
-
- io->written = 0;
- }
-
- if (b < 0)
- return 1;
-
- if (c < 0)
- return 2;
-
- return 3;
-
-}
-
-ssize_t __guac_write_base64_byte(GUACIO* io, char buf) {
-
- int* ready_buf = io->ready_buf;
-
- int retval;
-
- ready_buf[io->ready++] = buf & 0xFF;
-
- /* Flush triplet */
- if (io->ready == 3) {
- retval = __guac_write_base64_triplet(io, ready_buf[0], ready_buf[1], ready_buf[2]);
- if (retval < 0)
- return retval;
-
- io->ready = 0;
- }
-
- return 1;
-}
-
-ssize_t guac_write_base64(GUACIO* io, const void* buf, size_t count) {
-
- int retval;
-
- const char* char_buf = (const char*) buf;
- const char* end = char_buf + count;
-
- while (char_buf < end) {
-
- retval = __guac_write_base64_byte(io, *(char_buf++));
- if (retval < 0)
- return retval;
-
- }
-
- return count;
-
-}
-
-ssize_t guac_flush(GUACIO* io) {
-
- int retval;
-
- /* Flush remaining bytes in buffer */
- if (io->written > 0) {
- retval = __guac_write(io, io->out_buf, io->written);
- if (retval < 0)
- return retval;
-
- io->written = 0;
- }
-
- return 0;
-
-}
-
-ssize_t guac_flush_base64(GUACIO* io) {
-
- int retval;
-
- /* Flush triplet to output buffer */
- while (io->ready > 0) {
- retval = __guac_write_base64_byte(io, -1);
- if (retval < 0)
- return retval;
- }
-
- return 0;
-
-}
-
-
-int guac_select(GUACIO* io, int usec_timeout) {
-
- fd_set fds;
- struct timeval timeout;
-
- if (usec_timeout < 0)
- return select(io->fd + 1, &fds, NULL, NULL, NULL);
-
- timeout.tv_sec = usec_timeout/1000000;
- timeout.tv_usec = usec_timeout%1000000;
-
- FD_ZERO(&fds);
- FD_SET(io->fd, &fds);
-
- return select(io->fd + 1, &fds, NULL, NULL, &timeout);
-
-}
-
+++ /dev/null
-
-/*
- * Guacamole - Clientless Remote Desktop
- * Copyright (C) 2010 Michael Jumper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <png.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <uuid/uuid.h>
-
-#include "guacio.h"
-#include "protocol.h"
-
-char* guac_escape_string(const char* str) {
-
- char* escaped;
- char* current;
-
- int i;
- int length = 0;
-
- /* Determine length */
- for (i=0; str[i] != '\0'; i++) {
- switch (str[i]) {
-
- case ';':
- case ',':
- case '\\':
- length += 2;
- break;
-
- default:
- length++;
-
- }
- }
-
- /* Allocate new */
- escaped = malloc(length+1);
-
- current = escaped;
- for (i=0; str[i] != '\0'; i++) {
- switch (str[i]) {
-
- case ';':
- *(current++) = '\\';
- *(current++) = 's';
- break;
-
- case ',':
- *(current++) = '\\';
- *(current++) = 'c';
- break;
-
- case '\\':
- *(current++) = '\\';
- *(current++) = '\\';
- break;
-
- default:
- *(current++) = str[i];
- }
-
- }
-
- *current = '\0';
-
- return escaped;
-
-}
-
-char* guac_unescape_string_inplace(char* str) {
-
- char* from;
- char* to;
-
- from = to = str;
- for (;;) {
-
- char c = *(from++);
-
- if (c == '\\') {
-
- c = *(from++);
- if (c == 's')
- *(to++) = ';';
-
- else if (c == 'c')
- *(to++) = ',';
-
- else if (c == '\\')
- *(to++) = '\\';
-
- else if (c == '\0') {
- *(to++) = '\\';
- break;
- }
-
- else {
- *(to++) = '\\';
- *(to++) = c;
- }
- }
-
- else if (c == '\0')
- break;
-
- else
- *(to++) = c;
-
- }
-
- *to = '\0';
-
- return str;
-
-}
-
-void guac_send_name(GUACIO* io, const char* name) {
-
- char* escaped = guac_escape_string(name);
-
- guac_write_string(io, "name:");
- guac_write_string(io, escaped);
- guac_write_string(io, ";");
-
- free(escaped);
-
-}
-
-unsigned char __guac_get_base64_char_value(char c) {
-
- if (c >= 'A' && c <= 'Z')
- return c - 'A';
-
- if (c >= 'a' && c <= 'z')
- return c - 'a' + 26;
-
- if (c >= '0' && c <= '9')
- return c - '0' + 52;
-
- if (c == '+')
- return 62;
-
- if (c == '/')
- return 63;
-
- return 0;
-
-}
-
-char* guac_decode_base64_inplace(char* str) {
-
- char* from;
- char* to;
-
- from = to = str;
- for (;;) {
-
- unsigned char v1, v2, v3, v4;
- char first = *(from++);
-
- if (first == '\0')
- break;
-
- v1 = __guac_get_base64_char_value(first);
- v2 = __guac_get_base64_char_value(*(from++));
- v3 = __guac_get_base64_char_value(*(from++));
- v4 = __guac_get_base64_char_value(*(from++));
-
- *(to++) = (char) ((v1 << 2) | (v2 >> 4));
- *(to++) = (char) ((v2 << 4) | (v3 >> 2));
- *(to++) = (char) ((v3 << 6) | v4);
-
- }
-
- *to = '\0';
-
- return str;
-
-}
-
-void guac_send_size(GUACIO* io, int w, int h) {
- guac_write_string(io, "size:");
- guac_write_int(io, w);
- guac_write_string(io, ",");
- guac_write_int(io, h);
- guac_write_string(io, ";");
-}
-
-void guac_send_uuid(GUACIO* io, uuid_t uuid) {
-
- guac_write_string(io, "uuid:");
- guac_write_base64(io, uuid, 16);
- guac_flush_base64(io);
- guac_write_string(io, ";");
-
-}
-
-void guac_send_clipboard(GUACIO* io, const char* data) {
-
- char* escaped = guac_escape_string(data);
-
- guac_write_string(io, "clipboard:");
- guac_write_string(io, escaped);
- guac_write_string(io, ";");
-
- free(escaped);
-
-}
-
-void guac_send_error(GUACIO* io, const char* error) {
-
- char* escaped = guac_escape_string(error);
-
- guac_write_string(io, "error:");
- guac_write_string(io, escaped);
- guac_write_string(io, ";");
-
- free(escaped);
-
-}
-
-void guac_send_copy(GUACIO* io, int srcx, int srcy, int w, int h, int dstx, int dsty) {
- guac_write_string(io, "copy:");
- guac_write_int(io, srcx);
- guac_write_string(io, ",");
- guac_write_int(io, srcy);
- guac_write_string(io, ",");
- guac_write_int(io, w);
- guac_write_string(io, ",");
- guac_write_int(io, h);
- guac_write_string(io, ",");
- guac_write_int(io, dstx);
- guac_write_string(io, ",");
- guac_write_int(io, dsty);
- guac_write_string(io, ";");
-}
-
-void __guac_write_png(png_structp png, png_bytep data, png_size_t length) {
-
- if (guac_write_base64((GUACIO*) png->io_ptr, data, length) < 0) {
- perror("Error writing PNG");
- png_error(png, "Error writing PNG");
- return;
- }
-
-}
-
-void __guac_write_flush(png_structp png) {
-}
-
-void guac_send_png(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) {
-
- png_structp png;
- png_infop png_info;
-
- /* Write image */
-
- /* Set up PNG writer */
- png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png) {
- perror("Error initializing libpng write structure");
- return;
- }
-
- png_info = png_create_info_struct(png);
- if (!png_info) {
- perror("Error initializing libpng info structure");
- png_destroy_write_struct(&png, NULL);
- return;
- }
-
- /* Set error handler */
- if (setjmp(png_jmpbuf(png))) {
- perror("Error setting handler");
- png_destroy_write_struct(&png, &png_info);
- return;
- }
-
- png_set_write_fn(png, io, __guac_write_png, __guac_write_flush);
-
- /* Set PNG IHDR */
- png_set_IHDR(
- png,
- png_info,
- w,
- h,
- 8,
- PNG_COLOR_TYPE_RGB,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT
- );
-
- guac_write_string(io, "png:");
- guac_write_int(io, x);
- guac_write_string(io, ",");
- guac_write_int(io, y);
- guac_write_string(io, ",");
- png_set_rows(png, png_info, png_rows);
- png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL);
-
- if (guac_flush_base64(io) < 0) {
- perror("Error flushing PNG");
- png_error(png, "Error flushing PNG");
- return;
- }
-
- png_destroy_write_struct(&png, &png_info);
-
- guac_write_string(io, ";");
-
-}
-
-
-void guac_send_cursor(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) {
-
- png_structp png;
- png_infop png_info;
-
- /* Write image */
-
- /* Set up PNG writer */
- png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png) {
- perror("Error initializing libpng write structure");
- return;
- }
-
- png_info = png_create_info_struct(png);
- if (!png_info) {
- perror("Error initializing libpng info structure");
- png_destroy_write_struct(&png, NULL);
- return;
- }
-
- /* Set error handler */
- if (setjmp(png_jmpbuf(png))) {
- perror("Error setting handler");
- png_destroy_write_struct(&png, &png_info);
- return;
- }
-
- png_set_write_fn(png, io, __guac_write_png, __guac_write_flush);
-
- /* Set PNG IHDR */
- png_set_IHDR(
- png,
- png_info,
- w,
- h,
- 8,
- PNG_COLOR_TYPE_RGBA,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT
- );
-
- guac_write_string(io, "cursor:");
- guac_write_int(io, x);
- guac_write_string(io, ",");
- guac_write_int(io, y);
- guac_write_string(io, ",");
- png_set_rows(png, png_info, png_rows);
- png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL);
-
- if (guac_flush_base64(io) < 0) {
- perror("Error flushing PNG");
- png_error(png, "Error flushing PNG");
- return;
- }
-
- png_destroy_write_struct(&png, &png_info);
-
- guac_write_string(io, ";");
-
-}
-
-
-int __guac_fill_instructionbuf(GUACIO* io) {
-
- int retval;
-
- /* Attempt to fill buffer */
- retval = recv(
- io->fd,
- io->instructionbuf + io->instructionbuf_used_length,
- io->instructionbuf_size - io->instructionbuf_used_length,
- 0
- );
-
- if (retval < 0)
- return retval;
-
- io->instructionbuf_used_length += retval;
-
- /* Expand buffer if necessary */
- if (io->instructionbuf_used_length > io->instructionbuf_size / 2) {
- io->instructionbuf_size *= 2;
- io->instructionbuf = realloc(io->instructionbuf, io->instructionbuf_size);
- }
-
- return retval;
-
-}
-
-/* Returns new instruction if one exists, or NULL if no more instructions. */
-int guac_read_instruction(GUACIO* io, guac_instruction* parsed_instruction) {
-
- int retval;
- int i = 0;
- int argcount = 0;
- int j;
- int current_arg = 0;
-
- /* Loop until a instruction is read */
- for (;;) {
-
- /* Search for end of instruction */
- for (; i < io->instructionbuf_used_length; i++) {
-
- /* Count arguments as we look for the end */
- if (io->instructionbuf[i] == ',')
- argcount++;
- else if (io->instructionbuf[i] == ':' && argcount == 0)
- argcount++;
-
- /* End found ... */
- else if (io->instructionbuf[i] == ';') {
-
- /* Parse new instruction */
- char* instruction = malloc(i+1);
- memcpy(instruction, io->instructionbuf, i+1);
- instruction[i] = '\0'; /* Replace semicolon with null terminator. */
-
- parsed_instruction->opcode = NULL;
-
- parsed_instruction->argc = argcount;
- parsed_instruction->argv = malloc(sizeof(char*) * argcount);
-
- for (j=0; j<i; j++) {
-
- /* If encountered a colon, and no opcode parsed yet, set opcode and following argument */
- if (instruction[j] == ':' && parsed_instruction->opcode == NULL) {
- instruction[j] = '\0';
- parsed_instruction->argv[current_arg++] = &(instruction[j+1]);
- parsed_instruction->opcode = instruction;
- }
-
- /* If encountered a comma, set following argument */
- else if (instruction[j] == ',') {
- instruction[j] = '\0';
- parsed_instruction->argv[current_arg++] = &(instruction[j+1]);
- }
- }
-
- /* If no arguments, set opcode to entire instruction */
- if (parsed_instruction->opcode == NULL)
- parsed_instruction->opcode = instruction;
-
- /* Found. Reset buffer */
- memmove(io->instructionbuf, io->instructionbuf + i + 1, io->instructionbuf_used_length - i - 1);
- io->instructionbuf_used_length -= i + 1;
-
- /* Done */
- return 1;
- }
-
- }
-
- /* No instruction yet? Get more data ... */
- retval = guac_select(io, 1000);
- if (retval <= 0)
- return retval;
-
- retval = __guac_fill_instructionbuf(io);
- if (retval < 0)
- return retval;
-
- if (retval == 0)
- return -1; /* EOF */
- }
-
-}
-
-void guac_free_instruction(guac_instruction* instruction) {
- free(instruction->opcode);
-
- if (instruction->argv)
- free(instruction->argv);
-
- free(instruction);
-}
-
-
-int guac_instructions_waiting(GUACIO* io) {
-
- if (io->instructionbuf_used_length > 0)
- return 1;
-
- return guac_select(io, 1000);
-}
-
--- /dev/null
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include <syslog.h>
+
+#include "guacio.h"
+#include "protocol.h"
+#include "client.h"
+
+
+png_byte** guac_alloc_png_buffer(int w, int h, int bpp) {
+
+ png_byte** png_buffer;
+ png_byte* row;
+ int y;
+
+ /* Allocate rows for PNG */
+ png_buffer = (png_byte**) malloc(h * sizeof(png_byte*));
+ for (y=0; y<h; y++) {
+ row = (png_byte*) malloc(sizeof(png_byte) * bpp * w);
+ png_buffer[y] = row;
+ }
+
+ return png_buffer;
+}
+
+void guac_free_png_buffer(png_byte** png_buffer, int h) {
+
+ int y;
+
+ /* Free PNG data */
+ for (y = 0; y<h; y++)
+ free(png_buffer[y]);
+ free(png_buffer);
+
+}
+
+guac_client* __guac_alloc_client(GUACIO* io) {
+
+ /* Allocate new client (not handoff) */
+ guac_client* client = malloc(sizeof(guac_client));
+ memset(client, 0, sizeof(guac_client));
+
+ /* Init new client */
+ client->io = io;
+ uuid_generate(client->uuid);
+
+ return client;
+}
+
+
+guac_client* guac_get_client(int client_fd, guac_client_registry* registry, guac_client_init_handler* client_init, int argc, char** argv) {
+
+ guac_client* client;
+ GUACIO* io = guac_open(client_fd);
+ guac_instruction instruction;
+
+ /* Make copies of arguments */
+ char** safe_argv = malloc(argc * sizeof(char*));
+ char** scratch_argv = malloc(argc * sizeof(char*));
+
+ int i;
+ for (i=0; i<argc; i++)
+ scratch_argv[i] = safe_argv[i] = strdup(argv[i]);
+
+ /* Wait for handshaking messages */
+ for (;;) {
+
+ int retval;
+ retval = guac_read_instruction(io, &instruction); /* 0 if no instructions finished yet, <0 if error or EOF */
+
+ if (retval > 0) {
+
+ /* connect -> create new client connection */
+ if (strcmp(instruction.opcode, "connect") == 0) {
+
+ /* Create new client */
+ client = __guac_alloc_client(io);
+
+ /* Register client */
+ if (registry) {
+ guac_register_client(registry, client);
+
+ /* Send UUID to web-client */
+ guac_send_uuid(io, client->uuid);
+ guac_flush(client->io);
+ }
+
+ if (client_init(client, argc, scratch_argv) != 0)
+ return NULL;
+
+ break;
+ }
+
+ /* TODO: implement handoff */
+
+ }
+
+ if (retval < 0)
+ return NULL; /* EOF or error */
+
+ /* Otherwise, retval == 0 implies unfinished instruction */
+
+ }
+
+ /* Free memory used for arg copy */
+ for (i=0; i<argc; i++)
+ free(safe_argv[i]);
+
+ free(safe_argv);
+ free(scratch_argv);
+
+ return client;
+
+}
+
+
+void guac_free_client(guac_client* client, guac_client_registry* registry) {
+
+ if (client->free_handler) {
+ if (client->free_handler(client))
+ syslog(LOG_ERR, "Error calling client free handler");
+ }
+
+ guac_close(client->io);
+
+ guac_remove_client(registry, client->uuid);
+
+ free(client);
+}
+
+
+void guac_start_client(guac_client* client) {
+
+ guac_instruction instruction;
+ GUACIO* io = client->io;
+ int wait_result;
+
+ /* VNC Client Loop */
+ for (;;) {
+
+ /* Handle server messages */
+ if (client->handle_messages) {
+
+ int retval = client->handle_messages(client);
+ if (retval) {
+ syslog(LOG_ERR, "Error handling server messages");
+ return;
+ }
+
+ guac_flush(client->io);
+ }
+
+ wait_result = guac_instructions_waiting(io);
+ if (wait_result > 0) {
+
+ int retval;
+ retval = guac_read_instruction(io, &instruction); /* 0 if no instructions finished yet, <0 if error or EOF */
+
+ if (retval > 0) {
+
+ do {
+
+ if (strcmp(instruction.opcode, "mouse") == 0) {
+ if (client->mouse_handler)
+ if (
+ client->mouse_handler(
+ client,
+ atoi(instruction.argv[0]), /* x */
+ atoi(instruction.argv[1]), /* y */
+ atoi(instruction.argv[2]) /* mask */
+ )
+ ) {
+
+ syslog(LOG_ERR, "Error handling mouse instruction");
+ return;
+
+ }
+ }
+
+ else if (strcmp(instruction.opcode, "key") == 0) {
+ if (client->key_handler)
+ if (
+ client->key_handler(
+ client,
+ atoi(instruction.argv[0]), /* keysym */
+ atoi(instruction.argv[1]) /* pressed */
+ )
+ ) {
+
+ syslog(LOG_ERR, "Error handling key instruction");
+ return;
+
+ }
+ }
+
+ else if (strcmp(instruction.opcode, "clipboard") == 0) {
+ if (client->clipboard_handler)
+ if (
+ client->clipboard_handler(
+ client,
+ guac_unescape_string_inplace(instruction.argv[0]) /* data */
+ )
+ ) {
+
+ syslog(LOG_ERR, "Error handling clipboard instruction");
+ return;
+
+ }
+ }
+
+ else if (strcmp(instruction.opcode, "disconnect") == 0) {
+ syslog(LOG_INFO, "Client requested disconnect");
+ return;
+ }
+
+ } while ((retval = guac_read_instruction(io, &instruction)) > 0);
+
+ if (retval < 0) {
+ syslog(LOG_ERR, "Error reading instruction from stream");
+ return;
+ }
+ }
+
+ if (retval < 0) {
+ syslog(LOG_ERR, "Error or end of stream");
+ return; /* EOF or error */
+ }
+
+ /* Otherwise, retval == 0 implies unfinished instruction */
+
+ }
+ else if (wait_result < 0) {
+ syslog(LOG_ERR, "Error waiting for next instruction");
+ return;
+ }
+
+ }
+
+}
+
--- /dev/null
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CLIENTREG_H
+#define _CLIENTREG_H
+
+#include <stdlib.h>
+#include <uuid/uuid.h>
+
+#include "uuidtree.h"
+#include "client.h"
+
+guac_client_registry* guac_create_client_registry() {
+
+ guac_client_registry* registry = malloc(sizeof(guac_client_registry));
+
+ registry->root = guac_create_uuid_tree();
+
+ return registry;
+
+}
+
+void guac_register_client(guac_client_registry* registry, guac_client* client) {
+ guac_uuid_tree_put(registry->root, client->uuid, client);
+}
+
+guac_client* guac_find_client(guac_client_registry* registry, uuid_t uuid) {
+ return (guac_client*) guac_uuid_tree_get(registry->root, uuid);
+}
+
+void guac_remove_client(guac_client_registry* registry, uuid_t uuid) {
+ guac_uuid_tree_remove(registry->root, uuid);
+}
+
+void guac_cleanup_registry(guac_client_registry* registry) {
+ guac_cleanup_uuid_tree(registry->root);
+}
+
+#endif
--- /dev/null
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <time.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+#include "guacio.h"
+
+char __GUACIO_BAS64_CHARACTERS[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
+};
+
+GUACIO* guac_open(int fd) {
+
+ GUACIO* io = malloc(sizeof(GUACIO));
+ io->ready = 0;
+ io->written = 0;
+ io->fd = fd;
+
+ /* Allocate instruction buffer */
+ io->instructionbuf_size = 1024;
+ io->instructionbuf = malloc(io->instructionbuf_size);
+ io->instructionbuf_used_length = 0;
+
+ /* Set limit */
+ io->transfer_limit = 0;
+
+ return io;
+
+}
+
+void guac_close(GUACIO* io) {
+ guac_flush(io);
+ free(io);
+}
+
+
+/* Write bytes, limit rate */
+ssize_t __guac_write(GUACIO* io, const char* buf, int count) {
+
+ struct timeval start, end;
+ int retval;
+
+ /* Write and time how long the write takes (microseconds) */
+ gettimeofday(&start, NULL);
+ retval = write(io->fd, buf, count);
+ gettimeofday(&end, NULL);
+
+ if (retval < 0)
+ return retval;
+
+ if (io->transfer_limit > 0) {
+
+ suseconds_t elapsed;
+ suseconds_t required_usecs;
+
+ /* Get elapsed time */
+ elapsed = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec;
+
+ /* Calculate how much time we must sleep */
+ required_usecs = retval * 1000 / io->transfer_limit - elapsed; /* useconds at transfer_limit KB/s*/
+
+ /* Sleep as necessary */
+ if (required_usecs > 0) {
+
+ struct timespec required_sleep;
+
+ required_sleep.tv_sec = required_usecs / 1000000;
+ required_sleep.tv_nsec = (required_usecs % 1000000) * 1000;
+
+ nanosleep(&required_sleep, NULL);
+
+ }
+
+ }
+
+ return retval;
+}
+
+ssize_t guac_write_int(GUACIO* io, unsigned int i) {
+
+ char buffer[128];
+ char* ptr = &(buffer[127]);
+
+ *ptr = 0;
+
+ do {
+
+ ptr--;
+ *ptr = '0' + (i % 10);
+
+ i /= 10;
+
+ } while (i > 0 && ptr >= buffer);
+
+ return guac_write_string(io, ptr);
+
+}
+
+ssize_t guac_write_string(GUACIO* io, const char* str) {
+
+ char* out_buf = io->out_buf;
+
+ int retval;
+
+ for (; *str != '\0'; str++) {
+
+ out_buf[io->written++] = *str;
+
+ /* Flush when necessary, return on error */
+ if (io->written > 8188 /* sizeof(out_buf) - 4 */) {
+
+ struct timeval start, end;
+ suseconds_t elapsed;
+
+ gettimeofday(&start, NULL);
+ retval = __guac_write(io, out_buf, io->written);
+ gettimeofday(&end, NULL);
+
+ if (retval < 0)
+ return retval;
+
+ /* Get elapsed time */
+ elapsed = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec;
+
+ io->written = 0;
+ }
+
+ }
+
+ return 0;
+
+}
+
+ssize_t __guac_write_base64_triplet(GUACIO* io, int a, int b, int c) {
+
+ char* out_buf = io->out_buf;
+
+ int retval;
+
+ /* Byte 1 */
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[(a & 0xFC) >> 2]; /* [AAAAAA]AABBBB BBBBCC CCCCCC */
+
+ if (b >= 0) {
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((a & 0x03) << 4) | ((b & 0xF0) >> 4)]; /* AAAAAA[AABBBB]BBBBCC CCCCCC */
+
+ if (c >= 0) {
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)]; /* AAAAAA AABBBB[BBBBCC]CCCCCC */
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[c & 0x3F]; /* AAAAAA AABBBB BBBBCC[CCCCCC] */
+ }
+ else {
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((b & 0x0F) << 2)]; /* AAAAAA AABBBB[BBBB--]------ */
+ out_buf[io->written++] = '='; /* AAAAAA AABBBB BBBB--[------] */
+ }
+ }
+ else {
+ out_buf[io->written++] = __GUACIO_BAS64_CHARACTERS[((a & 0x03) << 4)]; /* AAAAAA[AA----]------ ------ */
+ out_buf[io->written++] = '='; /* AAAAAA AA----[------]------ */
+ out_buf[io->written++] = '='; /* AAAAAA AA---- ------[------] */
+ }
+
+ /* At this point, 4 bytes have been io->written */
+
+ /* Flush when necessary, return on error */
+ if (io->written > 8188 /* sizeof(out_buf) - 4 */) {
+ retval = __guac_write(io, out_buf, io->written);
+ if (retval < 0)
+ return retval;
+
+ io->written = 0;
+ }
+
+ if (b < 0)
+ return 1;
+
+ if (c < 0)
+ return 2;
+
+ return 3;
+
+}
+
+ssize_t __guac_write_base64_byte(GUACIO* io, char buf) {
+
+ int* ready_buf = io->ready_buf;
+
+ int retval;
+
+ ready_buf[io->ready++] = buf & 0xFF;
+
+ /* Flush triplet */
+ if (io->ready == 3) {
+ retval = __guac_write_base64_triplet(io, ready_buf[0], ready_buf[1], ready_buf[2]);
+ if (retval < 0)
+ return retval;
+
+ io->ready = 0;
+ }
+
+ return 1;
+}
+
+ssize_t guac_write_base64(GUACIO* io, const void* buf, size_t count) {
+
+ int retval;
+
+ const char* char_buf = (const char*) buf;
+ const char* end = char_buf + count;
+
+ while (char_buf < end) {
+
+ retval = __guac_write_base64_byte(io, *(char_buf++));
+ if (retval < 0)
+ return retval;
+
+ }
+
+ return count;
+
+}
+
+ssize_t guac_flush(GUACIO* io) {
+
+ int retval;
+
+ /* Flush remaining bytes in buffer */
+ if (io->written > 0) {
+ retval = __guac_write(io, io->out_buf, io->written);
+ if (retval < 0)
+ return retval;
+
+ io->written = 0;
+ }
+
+ return 0;
+
+}
+
+ssize_t guac_flush_base64(GUACIO* io) {
+
+ int retval;
+
+ /* Flush triplet to output buffer */
+ while (io->ready > 0) {
+ retval = __guac_write_base64_byte(io, -1);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+
+}
+
+
+int guac_select(GUACIO* io, int usec_timeout) {
+
+ fd_set fds;
+ struct timeval timeout;
+
+ if (usec_timeout < 0)
+ return select(io->fd + 1, &fds, NULL, NULL, NULL);
+
+ timeout.tv_sec = usec_timeout/1000000;
+ timeout.tv_usec = usec_timeout%1000000;
+
+ FD_ZERO(&fds);
+ FD_SET(io->fd, &fds);
+
+ return select(io->fd + 1, &fds, NULL, NULL, &timeout);
+
+}
+
--- /dev/null
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <png.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <uuid/uuid.h>
+
+#include "guacio.h"
+#include "protocol.h"
+
+char* guac_escape_string(const char* str) {
+
+ char* escaped;
+ char* current;
+
+ int i;
+ int length = 0;
+
+ /* Determine length */
+ for (i=0; str[i] != '\0'; i++) {
+ switch (str[i]) {
+
+ case ';':
+ case ',':
+ case '\\':
+ length += 2;
+ break;
+
+ default:
+ length++;
+
+ }
+ }
+
+ /* Allocate new */
+ escaped = malloc(length+1);
+
+ current = escaped;
+ for (i=0; str[i] != '\0'; i++) {
+ switch (str[i]) {
+
+ case ';':
+ *(current++) = '\\';
+ *(current++) = 's';
+ break;
+
+ case ',':
+ *(current++) = '\\';
+ *(current++) = 'c';
+ break;
+
+ case '\\':
+ *(current++) = '\\';
+ *(current++) = '\\';
+ break;
+
+ default:
+ *(current++) = str[i];
+ }
+
+ }
+
+ *current = '\0';
+
+ return escaped;
+
+}
+
+char* guac_unescape_string_inplace(char* str) {
+
+ char* from;
+ char* to;
+
+ from = to = str;
+ for (;;) {
+
+ char c = *(from++);
+
+ if (c == '\\') {
+
+ c = *(from++);
+ if (c == 's')
+ *(to++) = ';';
+
+ else if (c == 'c')
+ *(to++) = ',';
+
+ else if (c == '\\')
+ *(to++) = '\\';
+
+ else if (c == '\0') {
+ *(to++) = '\\';
+ break;
+ }
+
+ else {
+ *(to++) = '\\';
+ *(to++) = c;
+ }
+ }
+
+ else if (c == '\0')
+ break;
+
+ else
+ *(to++) = c;
+
+ }
+
+ *to = '\0';
+
+ return str;
+
+}
+
+void guac_send_name(GUACIO* io, const char* name) {
+
+ char* escaped = guac_escape_string(name);
+
+ guac_write_string(io, "name:");
+ guac_write_string(io, escaped);
+ guac_write_string(io, ";");
+
+ free(escaped);
+
+}
+
+unsigned char __guac_get_base64_char_value(char c) {
+
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A';
+
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 26;
+
+ if (c >= '0' && c <= '9')
+ return c - '0' + 52;
+
+ if (c == '+')
+ return 62;
+
+ if (c == '/')
+ return 63;
+
+ return 0;
+
+}
+
+char* guac_decode_base64_inplace(char* str) {
+
+ char* from;
+ char* to;
+
+ from = to = str;
+ for (;;) {
+
+ unsigned char v1, v2, v3, v4;
+ char first = *(from++);
+
+ if (first == '\0')
+ break;
+
+ v1 = __guac_get_base64_char_value(first);
+ v2 = __guac_get_base64_char_value(*(from++));
+ v3 = __guac_get_base64_char_value(*(from++));
+ v4 = __guac_get_base64_char_value(*(from++));
+
+ *(to++) = (char) ((v1 << 2) | (v2 >> 4));
+ *(to++) = (char) ((v2 << 4) | (v3 >> 2));
+ *(to++) = (char) ((v3 << 6) | v4);
+
+ }
+
+ *to = '\0';
+
+ return str;
+
+}
+
+void guac_send_size(GUACIO* io, int w, int h) {
+ guac_write_string(io, "size:");
+ guac_write_int(io, w);
+ guac_write_string(io, ",");
+ guac_write_int(io, h);
+ guac_write_string(io, ";");
+}
+
+void guac_send_uuid(GUACIO* io, uuid_t uuid) {
+
+ guac_write_string(io, "uuid:");
+ guac_write_base64(io, uuid, 16);
+ guac_flush_base64(io);
+ guac_write_string(io, ";");
+
+}
+
+void guac_send_clipboard(GUACIO* io, const char* data) {
+
+ char* escaped = guac_escape_string(data);
+
+ guac_write_string(io, "clipboard:");
+ guac_write_string(io, escaped);
+ guac_write_string(io, ";");
+
+ free(escaped);
+
+}
+
+void guac_send_error(GUACIO* io, const char* error) {
+
+ char* escaped = guac_escape_string(error);
+
+ guac_write_string(io, "error:");
+ guac_write_string(io, escaped);
+ guac_write_string(io, ";");
+
+ free(escaped);
+
+}
+
+void guac_send_copy(GUACIO* io, int srcx, int srcy, int w, int h, int dstx, int dsty) {
+ guac_write_string(io, "copy:");
+ guac_write_int(io, srcx);
+ guac_write_string(io, ",");
+ guac_write_int(io, srcy);
+ guac_write_string(io, ",");
+ guac_write_int(io, w);
+ guac_write_string(io, ",");
+ guac_write_int(io, h);
+ guac_write_string(io, ",");
+ guac_write_int(io, dstx);
+ guac_write_string(io, ",");
+ guac_write_int(io, dsty);
+ guac_write_string(io, ";");
+}
+
+void __guac_write_png(png_structp png, png_bytep data, png_size_t length) {
+
+ if (guac_write_base64((GUACIO*) png->io_ptr, data, length) < 0) {
+ perror("Error writing PNG");
+ png_error(png, "Error writing PNG");
+ return;
+ }
+
+}
+
+void __guac_write_flush(png_structp png) {
+}
+
+void guac_send_png(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) {
+
+ png_structp png;
+ png_infop png_info;
+
+ /* Write image */
+
+ /* Set up PNG writer */
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png) {
+ perror("Error initializing libpng write structure");
+ return;
+ }
+
+ png_info = png_create_info_struct(png);
+ if (!png_info) {
+ perror("Error initializing libpng info structure");
+ png_destroy_write_struct(&png, NULL);
+ return;
+ }
+
+ /* Set error handler */
+ if (setjmp(png_jmpbuf(png))) {
+ perror("Error setting handler");
+ png_destroy_write_struct(&png, &png_info);
+ return;
+ }
+
+ png_set_write_fn(png, io, __guac_write_png, __guac_write_flush);
+
+ /* Set PNG IHDR */
+ png_set_IHDR(
+ png,
+ png_info,
+ w,
+ h,
+ 8,
+ PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT
+ );
+
+ guac_write_string(io, "png:");
+ guac_write_int(io, x);
+ guac_write_string(io, ",");
+ guac_write_int(io, y);
+ guac_write_string(io, ",");
+ png_set_rows(png, png_info, png_rows);
+ png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL);
+
+ if (guac_flush_base64(io) < 0) {
+ perror("Error flushing PNG");
+ png_error(png, "Error flushing PNG");
+ return;
+ }
+
+ png_destroy_write_struct(&png, &png_info);
+
+ guac_write_string(io, ";");
+
+}
+
+
+void guac_send_cursor(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) {
+
+ png_structp png;
+ png_infop png_info;
+
+ /* Write image */
+
+ /* Set up PNG writer */
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png) {
+ perror("Error initializing libpng write structure");
+ return;
+ }
+
+ png_info = png_create_info_struct(png);
+ if (!png_info) {
+ perror("Error initializing libpng info structure");
+ png_destroy_write_struct(&png, NULL);
+ return;
+ }
+
+ /* Set error handler */
+ if (setjmp(png_jmpbuf(png))) {
+ perror("Error setting handler");
+ png_destroy_write_struct(&png, &png_info);
+ return;
+ }
+
+ png_set_write_fn(png, io, __guac_write_png, __guac_write_flush);
+
+ /* Set PNG IHDR */
+ png_set_IHDR(
+ png,
+ png_info,
+ w,
+ h,
+ 8,
+ PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT
+ );
+
+ guac_write_string(io, "cursor:");
+ guac_write_int(io, x);
+ guac_write_string(io, ",");
+ guac_write_int(io, y);
+ guac_write_string(io, ",");
+ png_set_rows(png, png_info, png_rows);
+ png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL);
+
+ if (guac_flush_base64(io) < 0) {
+ perror("Error flushing PNG");
+ png_error(png, "Error flushing PNG");
+ return;
+ }
+
+ png_destroy_write_struct(&png, &png_info);
+
+ guac_write_string(io, ";");
+
+}
+
+
+int __guac_fill_instructionbuf(GUACIO* io) {
+
+ int retval;
+
+ /* Attempt to fill buffer */
+ retval = recv(
+ io->fd,
+ io->instructionbuf + io->instructionbuf_used_length,
+ io->instructionbuf_size - io->instructionbuf_used_length,
+ 0
+ );
+
+ if (retval < 0)
+ return retval;
+
+ io->instructionbuf_used_length += retval;
+
+ /* Expand buffer if necessary */
+ if (io->instructionbuf_used_length > io->instructionbuf_size / 2) {
+ io->instructionbuf_size *= 2;
+ io->instructionbuf = realloc(io->instructionbuf, io->instructionbuf_size);
+ }
+
+ return retval;
+
+}
+
+/* Returns new instruction if one exists, or NULL if no more instructions. */
+int guac_read_instruction(GUACIO* io, guac_instruction* parsed_instruction) {
+
+ int retval;
+ int i = 0;
+ int argcount = 0;
+ int j;
+ int current_arg = 0;
+
+ /* Loop until a instruction is read */
+ for (;;) {
+
+ /* Search for end of instruction */
+ for (; i < io->instructionbuf_used_length; i++) {
+
+ /* Count arguments as we look for the end */
+ if (io->instructionbuf[i] == ',')
+ argcount++;
+ else if (io->instructionbuf[i] == ':' && argcount == 0)
+ argcount++;
+
+ /* End found ... */
+ else if (io->instructionbuf[i] == ';') {
+
+ /* Parse new instruction */
+ char* instruction = malloc(i+1);
+ memcpy(instruction, io->instructionbuf, i+1);
+ instruction[i] = '\0'; /* Replace semicolon with null terminator. */
+
+ parsed_instruction->opcode = NULL;
+
+ parsed_instruction->argc = argcount;
+ parsed_instruction->argv = malloc(sizeof(char*) * argcount);
+
+ for (j=0; j<i; j++) {
+
+ /* If encountered a colon, and no opcode parsed yet, set opcode and following argument */
+ if (instruction[j] == ':' && parsed_instruction->opcode == NULL) {
+ instruction[j] = '\0';
+ parsed_instruction->argv[current_arg++] = &(instruction[j+1]);
+ parsed_instruction->opcode = instruction;
+ }
+
+ /* If encountered a comma, set following argument */
+ else if (instruction[j] == ',') {
+ instruction[j] = '\0';
+ parsed_instruction->argv[current_arg++] = &(instruction[j+1]);
+ }
+ }
+
+ /* If no arguments, set opcode to entire instruction */
+ if (parsed_instruction->opcode == NULL)
+ parsed_instruction->opcode = instruction;
+
+ /* Found. Reset buffer */
+ memmove(io->instructionbuf, io->instructionbuf + i + 1, io->instructionbuf_used_length - i - 1);
+ io->instructionbuf_used_length -= i + 1;
+
+ /* Done */
+ return 1;
+ }
+
+ }
+
+ /* No instruction yet? Get more data ... */
+ retval = guac_select(io, 1000);
+ if (retval <= 0)
+ return retval;
+
+ retval = __guac_fill_instructionbuf(io);
+ if (retval < 0)
+ return retval;
+
+ if (retval == 0)
+ return -1; /* EOF */
+ }
+
+}
+
+void guac_free_instruction(guac_instruction* instruction) {
+ free(instruction->opcode);
+
+ if (instruction->argv)
+ free(instruction->argv);
+
+ free(instruction);
+}
+
+
+int guac_instructions_waiting(GUACIO* io) {
+
+ if (io->instructionbuf_used_length > 0)
+ return 1;
+
+ return guac_select(io, 1000);
+}
+
--- /dev/null
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "uuidtree.h"
+
+guac_uuid_tree_node* guac_create_uuid_tree() {
+
+ guac_uuid_tree_node* tree = malloc(sizeof(guac_uuid_tree_node));
+
+ tree->used = 0;
+ memset(tree->next, 0, sizeof(tree->next));
+
+ return tree;
+
+}
+
+void guac_uuid_tree_put(guac_uuid_tree_node* tree, uuid_t uuid, void* obj) {
+
+ guac_uuid_tree_node* current = tree;
+ int i;
+ unsigned char index;
+
+ for (i=0; i<sizeof(uuid_t)-1; i++) {
+
+ guac_uuid_tree_node* next;
+
+ /* Get next tree node */
+ index = ((unsigned char*) uuid)[i];
+ next = ((guac_uuid_tree_node**) current->next)[index];
+
+ /* If no node, allocate one */
+ if (next == NULL) {
+ current->used++;
+ next = guac_create_uuid_tree();
+ ((guac_uuid_tree_node**) current->next)[index] = next;
+ }
+
+ current = next;
+ }
+
+ /* Store object */
+ index = ((unsigned char*) uuid)[i];
+ current->next[index] = obj;
+
+}
+
+void* guac_uuid_tree_get(guac_uuid_tree_node* tree, uuid_t uuid) {
+
+ guac_uuid_tree_node* current = tree;
+ int i;
+ unsigned char index;
+
+ for (i=0; i<sizeof(uuid_t)-1; i++) {
+
+ /* Get next tree node */
+ index = ((unsigned char*) uuid)[i];
+ current = ((guac_uuid_tree_node**) current->next)[index];
+
+ /* If no node, not present */
+ if (current == NULL)
+ return NULL;
+
+ }
+
+ /* Return if found */
+ index = ((unsigned char*) uuid)[i];
+ return current->next[index];
+
+}
+
+void guac_uuid_tree_remove(guac_uuid_tree_node* tree, uuid_t uuid) {
+
+ guac_uuid_tree_node* current = tree;
+ int i;
+ unsigned char index;
+
+ for (i=0; i<sizeof(uuid_t)-1; i++) {
+
+ /* Get next tree node */
+ index = ((unsigned char*) uuid)[i];
+ current = ((guac_uuid_tree_node**) current->next)[index];
+
+ /* If no node, nothing to remove */
+ if (current == NULL)
+ return;
+
+ }
+
+ /* Remove, if present*/
+ if (current->next[index]) {
+ current->next[index] = NULL;
+ current->used--;
+
+ /* FIXME: If no more objects at this node, clean up */
+ if (current->used == 0) {
+ /* STUB */
+ }
+
+ }
+
+}
+
+void guac_cleanup_uuid_tree(guac_uuid_tree_node* tree) {
+
+ int i;
+ for (i=0; i<sizeof(tree->next); i++) {
+
+ if (tree->next[i] != NULL) {
+ guac_cleanup_uuid_tree(tree->next[i]);
+ tree->next[i] = NULL;
+ }
+
+ }
+
+ free(tree);
+
+}
+
+++ /dev/null
-
-/*
- * Guacamole - Clientless Remote Desktop
- * Copyright (C) 2010 Michael Jumper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <uuid/uuid.h>
-
-#include "uuidtree.h"
-
-guac_uuid_tree_node* guac_create_uuid_tree() {
-
- guac_uuid_tree_node* tree = malloc(sizeof(guac_uuid_tree_node));
-
- tree->used = 0;
- memset(tree->next, 0, sizeof(tree->next));
-
- return tree;
-
-}
-
-void guac_uuid_tree_put(guac_uuid_tree_node* tree, uuid_t uuid, void* obj) {
-
- guac_uuid_tree_node* current = tree;
- int i;
- unsigned char index;
-
- for (i=0; i<sizeof(uuid_t)-1; i++) {
-
- guac_uuid_tree_node* next;
-
- /* Get next tree node */
- index = ((unsigned char*) uuid)[i];
- next = ((guac_uuid_tree_node**) current->next)[index];
-
- /* If no node, allocate one */
- if (next == NULL) {
- current->used++;
- next = guac_create_uuid_tree();
- ((guac_uuid_tree_node**) current->next)[index] = next;
- }
-
- current = next;
- }
-
- /* Store object */
- index = ((unsigned char*) uuid)[i];
- current->next[index] = obj;
-
-}
-
-void* guac_uuid_tree_get(guac_uuid_tree_node* tree, uuid_t uuid) {
-
- guac_uuid_tree_node* current = tree;
- int i;
- unsigned char index;
-
- for (i=0; i<sizeof(uuid_t)-1; i++) {
-
- /* Get next tree node */
- index = ((unsigned char*) uuid)[i];
- current = ((guac_uuid_tree_node**) current->next)[index];
-
- /* If no node, not present */
- if (current == NULL)
- return NULL;
-
- }
-
- /* Return if found */
- index = ((unsigned char*) uuid)[i];
- return current->next[index];
-
-}
-
-void guac_uuid_tree_remove(guac_uuid_tree_node* tree, uuid_t uuid) {
-
- guac_uuid_tree_node* current = tree;
- int i;
- unsigned char index;
-
- for (i=0; i<sizeof(uuid_t)-1; i++) {
-
- /* Get next tree node */
- index = ((unsigned char*) uuid)[i];
- current = ((guac_uuid_tree_node**) current->next)[index];
-
- /* If no node, nothing to remove */
- if (current == NULL)
- return;
-
- }
-
- /* Remove, if present*/
- if (current->next[index]) {
- current->next[index] = NULL;
- current->used--;
-
- /* FIXME: If no more objects at this node, clean up */
- if (current->used == 0) {
- /* STUB */
- }
-
- }
-
-}
-
-void guac_cleanup_uuid_tree(guac_uuid_tree_node* tree) {
-
- int i;
- for (i=0; i<sizeof(tree->next); i++) {
-
- if (tree->next[i] != NULL) {
- guac_cleanup_uuid_tree(tree->next[i]);
- tree->next[i] = NULL;
- }
-
- }
-
- free(tree);
-
-}
-