Moved guac -> libguac, moving towards pluggable protocols.

This commit is contained in:
Michael Jumper
2010-09-13 01:14:08 -07:00
parent 279266a67d
commit 5b66e05f8d
13 changed files with 63 additions and 50 deletions

View File

@@ -1,27 +1,15 @@
CFLAGS=-O2 -pedantic -Wall -Werror
LDFLAGS=-lpng -lvncclient
CFLAGS=-O2 -pedantic -Wall -Werror -I../libguac/include
LDFLAGS=-L../libguac -lpng -lguac
.PHONY: clean
all: guacd
guacd: daemon.o client.o guacio.o protocol.o vnc_client.o
$(CC) $(CFLAGS) $(LDFLAGS) daemon.o client.o protocol.o guacio.o vnc_client.o -o guacd
guacd: daemon.o
$(CC) $(CFLAGS) $(LDFLAGS) daemon.o -o guacd
client.o: client.c client.h guacio.h
$(CC) $(CFLAGS) -c client.c
vnc_client.o: vnc_client.c vnc_client.h guacio.h
$(CC) $(CFLAGS) -c vnc_client.c
protocol.o: protocol.c protocol.h guacio.h
$(CC) $(CFLAGS) -c protocol.c
guacio.o: guacio.c guacio.h
$(CC) $(CFLAGS) -c guacio.c
daemon.o: daemon.c client.h vnc_client.h
daemon.o: daemon.c
$(CC) $(CFLAGS) -c daemon.c
clean:

View File

@@ -1,149 +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 "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_get_client(int client_fd, void (*client_init)(guac_client* client, const char* hostname, int port), const char* hostname, int port) {
guac_client* client = malloc(sizeof(guac_client));
client->io = guac_open(client_fd);
client_init(client, hostname, port);
guac_flush(client->io);
return client;
}
void guac_free_client(guac_client* client) {
if (client->free_handler)
client->free_handler(client);
guac_close(client->io);
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) {
client->handle_messages(client);
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)
client->mouse_handler(
client,
atoi(instruction.argv[0]), /* x */
atoi(instruction.argv[1]), /* y */
atoi(instruction.argv[2]) /* mask */
);
}
else if (strcmp(instruction.opcode, "key") == 0) {
if (client->key_handler)
client->key_handler(
client,
atoi(instruction.argv[0]), /* keysym */
atoi(instruction.argv[1]) /* pressed */
);
}
else if (strcmp(instruction.opcode, "clipboard") == 0) {
if (client->clipboard_handler)
client->clipboard_handler(
client,
guac_unescape_string_inplace(instruction.argv[0]) /* data */
);
}
} while ((retval = guac_read_instruction(io, &instruction)) > 0);
if (retval < 0)
return;
}
if (retval < 0)
return; /* EOF or error */
/* Otherwise, retval == 0 implies unfinished instruction */
}
else if (wait_result < 0)
return;
}
}

View File

@@ -1,46 +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 _CLIENT_H
#define _CLIENT_H
#include <png.h>
#include "guacio.h"
typedef struct guac_client {
GUACIO* io;
void* data;
void (*handle_messages)(struct guac_client* client);
void (*mouse_handler)(struct guac_client* client, int x, int y, int button_mask);
void (*key_handler)(struct guac_client* client, int keysym, int pressed);
void (*clipboard_handler)(struct guac_client* client, char* copied);
void (*free_handler)(void* client);
} guac_client;
guac_client* guac_get_client(int client_fd, void (*client_init)(guac_client* client, const char* hostname, int port), const char* hostname, int port);
void guac_start_client(guac_client* client);
void guac_free_client(guac_client* client);
png_byte** guac_alloc_png_buffer(int w, int h, int bpp);
void guac_free_png_buffer(png_byte** png_buffer, int h);
#endif

View File

@@ -26,7 +26,6 @@
#include <netinet/in.h>
#include "client.h"
#include "vnc_client.h"
int main(int argc, char* argv[]) {
@@ -107,7 +106,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "[guacamole] spawning client\n");
client = guac_get_client(connected_socket_fd, vnc_guac_client_init, connect_host, connect_port);
client = NULL; /*guac_get_client(connected_socket_fd, vnc_guac_client_init, connect_host, connect_port); */ /* STUB! */
guac_start_client(client);
guac_free_client(client);

View File

@@ -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);
}

View File

@@ -1,54 +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 _GUACIO_H
#define _GUACIO_H
#include <unistd.h>
typedef struct GUACIO {
int fd;
int ready;
int ready_buf[3];
int written;
char out_buf[8192];
int instructionbuf_size;
int instructionbuf_used_length;
char* instructionbuf;
/* Limit */
unsigned int transfer_limit; /* KB/sec */
} GUACIO;
GUACIO* guac_open(int fd);
ssize_t guac_write_int(GUACIO* io, unsigned int i);
ssize_t guac_write_string(GUACIO* io, const char* str);
ssize_t guac_write_base64(GUACIO* io, const void* buf, size_t count);
ssize_t guac_flush_base64(GUACIO* io);
ssize_t guac_flush(GUACIO* io);
int guac_select(GUACIO* io, int usec_timeout);
void guac_close(GUACIO* io);
#endif

View File

@@ -1,461 +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 "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);
}
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_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);
}

View File

@@ -1,52 +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 __PROTOCOL_H
#define __PROTOCOL_H
#include <png.h>
#include "guacio.h"
typedef struct guac_instruction {
char* opcode;
int argc;
char** argv;
} guac_instruction;
void guac_free_instruction(guac_instruction* instruction);
char* guac_escape_string(const char* str);
char* guac_unescape_string_inplace(char* str);
void guac_send_name(GUACIO* io, const char* name);
void guac_send_error(GUACIO* io, const char* error);
void guac_send_clipboard(GUACIO* io, const char* data);
void guac_send_size(GUACIO* io, int w, int h);
void guac_send_copy(GUACIO* io, int srcx, int srcy, int w, int h, int dstx, int dsty);
void guac_send_png(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h);
void guac_send_cursor(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h);
int guac_instructions_waiting(GUACIO* io);
int guac_read_instruction(GUACIO* io, guac_instruction* parsed_instruction);
#endif

View File

@@ -1,332 +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 <png.h>
#include <time.h>
#include <rfb/rfbclient.h>
#include "guacio.h"
#include "protocol.h"
#include "client.h"
char __guac_password[] = "potato";
static char* __GUAC_CLIENT = "GUAC_CLIENT";
typedef struct vnc_guac_client_data {
rfbClient* rfb_client;
png_byte** png_buffer;
png_byte** png_buffer_alpha;
int copy_rect_used;
} vnc_guac_client_data;
void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) {
int dx, dy;
guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
GUACIO* io = gc->io;
png_byte** png_buffer = ((vnc_guac_client_data*) gc->data)->png_buffer_alpha;
png_byte* row;
png_byte** png_row_current = png_buffer;
unsigned int bytesPerRow = bpp * w;
unsigned char* fb_row_current = client->rcSource;
unsigned char* fb_mask = client->rcMask;
unsigned char* fb_row;
unsigned int v;
/* Copy image data from VNC client to PNG */
for (dy = 0; dy<h; dy++) {
row = *(png_row_current++);
fb_row = fb_row_current;
fb_row_current += bytesPerRow;
for (dx = 0; dx<w; dx++) {
switch (bpp) {
case 4:
v = *((unsigned int*) fb_row);
break;
case 2:
v = *((unsigned short*) fb_row);
break;
default:
v = *((unsigned char*) fb_row);
}
*(row++) = (v >> client->format.redShift) * 256 / (client->format.redMax+1);
*(row++) = (v >> client->format.greenShift) * 256 / (client->format.greenMax+1);
*(row++) = (v >> client->format.blueShift) * 256 / (client->format.blueMax+1);
/* Handle mask */
if (*(fb_mask++))
*(row++) = 255;
else
*(row++) = 0;
fb_row += bpp;
}
}
/* SEND CURSOR */
guac_send_cursor(io, x, y, png_buffer, w, h);
}
void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) {
int dx, dy;
guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
GUACIO* io = gc->io;
png_byte** png_buffer = ((vnc_guac_client_data*) gc->data)->png_buffer;
png_byte* row;
png_byte** png_row_current = png_buffer;
unsigned int bpp = client->format.bitsPerPixel/8;
unsigned int bytesPerRow = bpp * client->width;
unsigned char* fb_row_current = client->frameBuffer + (y * bytesPerRow) + (x * bpp);
unsigned char* fb_row;
unsigned int v;
/* Ignore extra update if already handled by copyrect */
if (((vnc_guac_client_data*) gc->data)->copy_rect_used) {
((vnc_guac_client_data*) gc->data)->copy_rect_used = 0;
return;
}
/* Copy image data from VNC client to PNG */
for (dy = y; dy<y+h; dy++) {
row = *(png_row_current++);
fb_row = fb_row_current;
fb_row_current += bytesPerRow;
for (dx = x; dx<x+w; dx++) {
switch (bpp) {
case 4:
v = *((unsigned int*) fb_row);
break;
case 2:
v = *((unsigned short*) fb_row);
break;
default:
v = *((unsigned char*) fb_row);
}
*(row++) = (v >> client->format.redShift) * 256 / (client->format.redMax+1);
*(row++) = (v >> client->format.greenShift) * 256 / (client->format.greenMax+1);
*(row++) = (v >> client->format.blueShift) * 256 / (client->format.blueMax+1);
fb_row += bpp;
}
}
guac_send_png(io, x, y, png_buffer, w, h);
}
void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
GUACIO* io = gc->io;
guac_send_copy(io, src_x, src_y, w, h, dest_x, dest_y);
((vnc_guac_client_data*) gc->data)->copy_rect_used = 1;
}
char* guac_vnc_get_password(rfbClient* client) {
/* Freed after use by libvncclient */
char* password = malloc(64);
strncpy(password, __guac_password, 63);
return password;
}
void guac_vnc_cut_text(rfbClient* client, const char* text, int textlen) {
guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
GUACIO* io = gc->io;
guac_send_clipboard(io, text);
}
void vnc_guac_client_handle_messages(guac_client* client) {
int wait_result;
rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
wait_result = WaitForMessage(rfb_client, 2000);
if (wait_result < 0) {
fprintf(stderr, "WAIT FAIL\n");
return;
}
if (wait_result > 0) {
struct timespec sleep_period;
if (!HandleRFBServerMessage(rfb_client)) {
fprintf(stderr, "HANDLE FAIL\n");
return;
}
/* Wait before returning ... don't want to handle
* too many server messages. */
sleep_period.tv_sec = 0;
sleep_period.tv_nsec = 50000000L /* 50 ms */;
nanosleep(&sleep_period, NULL);
}
}
void vnc_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
SendPointerEvent(rfb_client, x, y, mask);
}
void vnc_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
SendKeyEvent(rfb_client, keysym, pressed);
}
void vnc_guac_client_clipboard_handler(guac_client* client, char* data) {
rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
SendClientCutText(rfb_client, data, strlen(data));
}
void vnc_guac_client_free_handler(guac_client* client) {
rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
png_byte** png_buffer = ((vnc_guac_client_data*) client->data)->png_buffer;
png_byte** png_buffer_alpha = ((vnc_guac_client_data*) client->data)->png_buffer_alpha;
/* Free PNG data */
guac_free_png_buffer(png_buffer, rfb_client->height);
guac_free_png_buffer(png_buffer_alpha, rfb_client->height);
/* Clean up VNC client*/
rfbClientCleanup(rfb_client);
}
void vnc_guac_client_init(guac_client* client, const char* hostname, int port) {
char* hostname_copy;
rfbClient* rfb_client;
png_byte** png_buffer;
png_byte** png_buffer_alpha;
vnc_guac_client_data* vnc_guac_client_data;
/*** INIT ***/
rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */
/* Framebuffer update handler */
rfb_client->GotFrameBufferUpdate = guac_vnc_update;
rfb_client->GotCopyRect = guac_vnc_copyrect;
/* Enable client-side cursor */
rfb_client->GotCursorShape = guac_vnc_cursor;
rfb_client->appData.useRemoteCursor = TRUE;
/* Clipboard */
rfb_client->GotXCutText = guac_vnc_cut_text;
/* Password */
rfb_client->GetPassword = guac_vnc_get_password;
/* Connect */
hostname_copy = malloc(1024);
strncpy(hostname_copy, hostname, 1024);
rfb_client->serverHost = hostname_copy;
rfb_client->serverPort = port;
rfbInitClient(rfb_client, NULL, NULL);
/* Allocate buffers */
png_buffer = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 3); /* No-alpha */
png_buffer_alpha = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 4); /* With alpha */
/* Store Guac data in client */
rfbClientSetClientData(rfb_client, __GUAC_CLIENT, client);
/* Set client data */
vnc_guac_client_data = malloc(sizeof(vnc_guac_client_data));
vnc_guac_client_data->rfb_client = rfb_client;
vnc_guac_client_data->png_buffer = png_buffer;
vnc_guac_client_data->png_buffer_alpha = png_buffer_alpha;
vnc_guac_client_data->copy_rect_used = 0;
client->data = vnc_guac_client_data;
/* Set handlers */
client->handle_messages = vnc_guac_client_handle_messages;
client->mouse_handler = vnc_guac_client_mouse_handler;
client->key_handler = vnc_guac_client_key_handler;
client->clipboard_handler = vnc_guac_client_clipboard_handler;
/* Send name */
guac_send_name(client->io, rfb_client->desktopName);
/* Send size */
guac_send_size(client->io, rfb_client->width, rfb_client->height);
}

View File

@@ -1,28 +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 _VNC_CLIENT_H
#define _VNC_CLIENT_H
#include "client.h"
void vnc_guac_client_init(guac_client* client, const char* hostname, int port);
#endif