Moved files to src/
authorMichael Jumper <zhangmaike@users.sourceforge.net>
Tue, 21 Sep 2010 22:39:18 +0000 (22:39 +0000)
committerMichael Jumper <zhangmaike@users.sourceforge.net>
Tue, 21 Sep 2010 22:39:18 +0000 (22:39 +0000)
12 files changed:
libguac/Makefile.am
libguac/client.c [deleted file]
libguac/clientreg.c [deleted file]
libguac/configure.in
libguac/guacio.c [deleted file]
libguac/protocol.c [deleted file]
libguac/src/client.c [new file with mode: 0644]
libguac/src/clientreg.c [new file with mode: 0644]
libguac/src/guacio.c [new file with mode: 0644]
libguac/src/protocol.c [new file with mode: 0644]
libguac/src/uuidtree.c [new file with mode: 0644]
libguac/uuidtree.c [deleted file]

index 9ecdeca..fbdfd02 100644 (file)
@@ -4,7 +4,7 @@ AM_CFLAGS = -Werror -Wall -Iinclude
 
 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
diff --git a/libguac/client.c b/libguac/client.c
deleted file mode 100644 (file)
index 4f6b5f6..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-
-/*
- *  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;
-        }
-
-    }
-
-}
-
diff --git a/libguac/clientreg.c b/libguac/clientreg.c
deleted file mode 100644 (file)
index fb3e13b..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-
-/*
- *  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
index 4badf38..535546f 100644 (file)
@@ -1,8 +1,8 @@
 #                                               -*- 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
diff --git a/libguac/guacio.c b/libguac/guacio.c
deleted file mode 100644 (file)
index 3deff95..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-
-/*
- *  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); 
-
-}
-
diff --git a/libguac/protocol.c b/libguac/protocol.c
deleted file mode 100644 (file)
index b5c81ca..0000000
+++ /dev/null
@@ -1,524 +0,0 @@
-
-/*
- *  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);
-}
-
diff --git a/libguac/src/client.c b/libguac/src/client.c
new file mode 100644 (file)
index 0000000..4f6b5f6
--- /dev/null
@@ -0,0 +1,262 @@
+
+/*
+ *  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;
+        }
+
+    }
+
+}
+
diff --git a/libguac/src/clientreg.c b/libguac/src/clientreg.c
new file mode 100644 (file)
index 0000000..fb3e13b
--- /dev/null
@@ -0,0 +1,55 @@
+
+/*
+ *  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
diff --git a/libguac/src/guacio.c b/libguac/src/guacio.c
new file mode 100644 (file)
index 0000000..3deff95
--- /dev/null
@@ -0,0 +1,298 @@
+
+/*
+ *  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); 
+
+}
+
diff --git a/libguac/src/protocol.c b/libguac/src/protocol.c
new file mode 100644 (file)
index 0000000..b5c81ca
--- /dev/null
@@ -0,0 +1,524 @@
+
+/*
+ *  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);
+}
+
diff --git a/libguac/src/uuidtree.c b/libguac/src/uuidtree.c
new file mode 100644 (file)
index 0000000..ca0e4cc
--- /dev/null
@@ -0,0 +1,139 @@
+
+/*
+ *  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);
+
+}
+
diff --git a/libguac/uuidtree.c b/libguac/uuidtree.c
deleted file mode 100644 (file)
index ca0e4cc..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-
-/*
- *  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);
-
-}
-