mirror of
https://github.com/debauchee/barrier.git
synced 2026-02-10 05:36:22 +08:00
event loop model. Streams, stream filters, and sockets are converted. Client proxies are almost converted. CServer is in progress. Removed all HTTP code. Haven't converted the necessary win32 arch stuff.
543 lines
12 KiB
C++
543 lines
12 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2002 Chris Schoeneman
|
|
*
|
|
* This package is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* found in the file COPYING that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "CProtocolUtil.h"
|
|
#include "IStream.h"
|
|
#include "CLog.h"
|
|
#include "stdvector.h"
|
|
#include <cctype>
|
|
#include <cstring>
|
|
|
|
//
|
|
// CProtocolUtil
|
|
//
|
|
|
|
void
|
|
CProtocolUtil::writef(IStream* stream, const char* fmt, ...)
|
|
{
|
|
assert(stream != NULL);
|
|
assert(fmt != NULL);
|
|
LOG((CLOG_DEBUG2 "writef(%s)", fmt));
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
UInt32 size = getLength(fmt, args);
|
|
va_end(args);
|
|
va_start(args, fmt);
|
|
vwritef(stream, fmt, size, args);
|
|
va_end(args);
|
|
}
|
|
|
|
bool
|
|
CProtocolUtil::readf(IStream* stream, const char* fmt, ...)
|
|
{
|
|
assert(stream != NULL);
|
|
assert(fmt != NULL);
|
|
LOG((CLOG_DEBUG2 "readf(%s)", fmt));
|
|
|
|
bool result;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
try {
|
|
vreadf(stream, fmt, args);
|
|
result = true;
|
|
}
|
|
catch (XIO&) {
|
|
result = false;
|
|
}
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
void
|
|
CProtocolUtil::vwritef(IStream* stream,
|
|
const char* fmt, UInt32 size, va_list args)
|
|
{
|
|
assert(stream != NULL);
|
|
assert(fmt != NULL);
|
|
|
|
// done if nothing to write
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
// fill buffer
|
|
// FIXME -- can we use alloca?
|
|
UInt8* buffer = new UInt8[size];
|
|
writef(buffer, fmt, args);
|
|
|
|
try {
|
|
// write buffer
|
|
stream->write(buffer, size);
|
|
LOG((CLOG_DEBUG2 "wrote %d bytes", size));
|
|
|
|
delete[] buffer;
|
|
}
|
|
catch (XBase&) {
|
|
delete[] buffer;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
bool
|
|
CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args)
|
|
{
|
|
assert(stream != NULL);
|
|
assert(fmt != NULL);
|
|
|
|
// begin scanning
|
|
while (*fmt) {
|
|
if (*fmt == '%') {
|
|
// format specifier. determine argument size.
|
|
++fmt;
|
|
UInt32 len = eatLength(&fmt);
|
|
switch (*fmt) {
|
|
case 'i': {
|
|
// check for valid length
|
|
assert(len == 1 || len == 2 || len == 4);
|
|
|
|
// read the data
|
|
UInt8 buffer[4];
|
|
read(stream, buffer, len);
|
|
|
|
// convert it
|
|
void* v = va_arg(args, void*);
|
|
switch (len) {
|
|
case 1:
|
|
// 1 byte integer
|
|
*reinterpret_cast<UInt8*>(v) = buffer[0];
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast<UInt8*>(v), *reinterpret_cast<UInt8*>(v)));
|
|
break;
|
|
|
|
case 2:
|
|
// 2 byte integer
|
|
*reinterpret_cast<UInt16*>(v) =
|
|
static_cast<UInt16>(
|
|
(static_cast<UInt16>(buffer[0]) << 8) |
|
|
static_cast<UInt16>(buffer[1]));
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast<UInt16*>(v), *reinterpret_cast<UInt16*>(v)));
|
|
break;
|
|
|
|
case 4:
|
|
// 4 byte integer
|
|
*reinterpret_cast<UInt32*>(v) =
|
|
(static_cast<UInt32>(buffer[0]) << 24) |
|
|
(static_cast<UInt32>(buffer[1]) << 16) |
|
|
(static_cast<UInt32>(buffer[2]) << 8) |
|
|
static_cast<UInt32>(buffer[3]);
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast<UInt32*>(v), *reinterpret_cast<UInt32*>(v)));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'I': {
|
|
// check for valid length
|
|
assert(len == 1 || len == 2 || len == 4);
|
|
|
|
// read the vector length
|
|
UInt8 buffer[4];
|
|
read(stream, buffer, 4);
|
|
UInt32 n = (static_cast<UInt32>(buffer[0]) << 24) |
|
|
(static_cast<UInt32>(buffer[1]) << 16) |
|
|
(static_cast<UInt32>(buffer[2]) << 8) |
|
|
static_cast<UInt32>(buffer[3]);
|
|
|
|
// convert it
|
|
void* v = va_arg(args, void*);
|
|
switch (len) {
|
|
case 1:
|
|
// 1 byte integer
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
read(stream, buffer, 1);
|
|
reinterpret_cast<std::vector<UInt8>*>(v)->push_back(
|
|
buffer[0]);
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast<std::vector<UInt8>*>(v)->back(), reinterpret_cast<std::vector<UInt8>*>(v)->back()));
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
// 2 byte integer
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
read(stream, buffer, 2);
|
|
reinterpret_cast<std::vector<UInt16>*>(v)->push_back(
|
|
static_cast<UInt16>(
|
|
(static_cast<UInt16>(buffer[0]) << 8) |
|
|
static_cast<UInt16>(buffer[1])));
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast<std::vector<UInt16>*>(v)->back(), reinterpret_cast<std::vector<UInt16>*>(v)->back()));
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
// 4 byte integer
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
read(stream, buffer, 4);
|
|
reinterpret_cast<std::vector<UInt32>*>(v)->push_back(
|
|
(static_cast<UInt32>(buffer[0]) << 24) |
|
|
(static_cast<UInt32>(buffer[1]) << 16) |
|
|
(static_cast<UInt32>(buffer[2]) << 8) |
|
|
static_cast<UInt32>(buffer[3]));
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast<std::vector<UInt32>*>(v)->back(), reinterpret_cast<std::vector<UInt32>*>(v)->back()));
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 's': {
|
|
assert(len == 0);
|
|
|
|
// read the string length
|
|
UInt8 buffer[128];
|
|
read(stream, buffer, 4);
|
|
UInt32 len = (static_cast<UInt32>(buffer[0]) << 24) |
|
|
(static_cast<UInt32>(buffer[1]) << 16) |
|
|
(static_cast<UInt32>(buffer[2]) << 8) |
|
|
static_cast<UInt32>(buffer[3]);
|
|
|
|
// use a fixed size buffer if its big enough
|
|
const bool useFixed = (len <= sizeof(buffer));
|
|
|
|
// allocate a buffer to read the data
|
|
UInt8* sBuffer;
|
|
if (useFixed) {
|
|
sBuffer = buffer;
|
|
}
|
|
else {
|
|
sBuffer = new UInt8[len];
|
|
}
|
|
|
|
// read the data
|
|
try {
|
|
read(stream, sBuffer, len);
|
|
}
|
|
catch (...) {
|
|
if (!useFixed) {
|
|
delete[] sBuffer;
|
|
}
|
|
throw;
|
|
}
|
|
LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer));
|
|
|
|
// save the data
|
|
CString* dst = va_arg(args, CString*);
|
|
dst->assign((const char*)sBuffer, len);
|
|
|
|
// release the buffer
|
|
if (!useFixed) {
|
|
delete[] sBuffer;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case '%':
|
|
assert(len == 0);
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "invalid format specifier");
|
|
}
|
|
|
|
// next format character
|
|
++fmt;
|
|
}
|
|
else {
|
|
// read next character
|
|
char buffer[1];
|
|
read(stream, buffer, 1);
|
|
|
|
// verify match
|
|
if (buffer[0] != *fmt) {
|
|
LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0]));
|
|
throw XIOReadMismatch();
|
|
}
|
|
|
|
// next format character
|
|
++fmt;
|
|
}
|
|
}
|
|
}
|
|
|
|
UInt32
|
|
CProtocolUtil::getLength(const char* fmt, va_list args)
|
|
{
|
|
UInt32 n = 0;
|
|
while (*fmt) {
|
|
if (*fmt == '%') {
|
|
// format specifier. determine argument size.
|
|
++fmt;
|
|
UInt32 len = eatLength(&fmt);
|
|
switch (*fmt) {
|
|
case 'i':
|
|
assert(len == 1 || len == 2 || len == 4);
|
|
(void)va_arg(args, UInt32);
|
|
break;
|
|
|
|
case 'I':
|
|
assert(len == 1 || len == 2 || len == 4);
|
|
switch (len) {
|
|
case 1:
|
|
len = (va_arg(args, std::vector<UInt8>*))->size() + 4;
|
|
break;
|
|
|
|
case 2:
|
|
len = 2 * (va_arg(args, std::vector<UInt16>*))->size() + 4;
|
|
break;
|
|
|
|
case 4:
|
|
len = 4 * (va_arg(args, std::vector<UInt32>*))->size() + 4;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
assert(len == 0);
|
|
len = (va_arg(args, CString*))->size() + 4;
|
|
(void)va_arg(args, UInt8*);
|
|
break;
|
|
|
|
case 'S':
|
|
assert(len == 0);
|
|
len = va_arg(args, UInt32) + 4;
|
|
(void)va_arg(args, UInt8*);
|
|
break;
|
|
|
|
case '%':
|
|
assert(len == 0);
|
|
len = 1;
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "invalid format specifier");
|
|
}
|
|
|
|
// accumulate size
|
|
n += len;
|
|
++fmt;
|
|
}
|
|
else {
|
|
// regular character
|
|
++n;
|
|
++fmt;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void
|
|
CProtocolUtil::writef(void* buffer, const char* fmt, va_list args)
|
|
{
|
|
UInt8* dst = reinterpret_cast<UInt8*>(buffer);
|
|
|
|
while (*fmt) {
|
|
if (*fmt == '%') {
|
|
// format specifier. determine argument size.
|
|
++fmt;
|
|
UInt32 len = eatLength(&fmt);
|
|
switch (*fmt) {
|
|
case 'i': {
|
|
const UInt32 v = va_arg(args, UInt32);
|
|
switch (len) {
|
|
case 1:
|
|
// 1 byte integer
|
|
*dst++ = static_cast<UInt8>(v & 0xff);
|
|
break;
|
|
|
|
case 2:
|
|
// 2 byte integer
|
|
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( v & 0xff);
|
|
break;
|
|
|
|
case 4:
|
|
// 4 byte integer
|
|
*dst++ = static_cast<UInt8>((v >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((v >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( v & 0xff);
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "invalid integer format length");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'I': {
|
|
switch (len) {
|
|
case 1: {
|
|
// 1 byte integers
|
|
const std::vector<UInt8>* list =
|
|
va_arg(args, const std::vector<UInt8>*);
|
|
const UInt32 n = list->size();
|
|
*dst++ = static_cast<UInt8>((n >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( n & 0xff);
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
*dst++ = (*list)[i];
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2: {
|
|
// 2 byte integers
|
|
const std::vector<UInt16>* list =
|
|
va_arg(args, const std::vector<UInt16>*);
|
|
const UInt32 n = list->size();
|
|
*dst++ = static_cast<UInt8>((n >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( n & 0xff);
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
const UInt16 v = (*list)[i];
|
|
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( v & 0xff);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 4: {
|
|
// 4 byte integers
|
|
const std::vector<UInt32>* list =
|
|
va_arg(args, const std::vector<UInt32>*);
|
|
const UInt32 n = list->size();
|
|
*dst++ = static_cast<UInt8>((n >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((n >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( n & 0xff);
|
|
for (UInt32 i = 0; i < n; ++i) {
|
|
const UInt32 v = (*list)[i];
|
|
*dst++ = static_cast<UInt8>((v >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((v >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( v & 0xff);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert(0 && "invalid integer vector format length");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 's': {
|
|
assert(len == 0);
|
|
const CString* src = va_arg(args, CString*);
|
|
const UInt32 len = (src != NULL) ? src->size() : 0;
|
|
*dst++ = static_cast<UInt8>((len >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((len >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((len >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( len & 0xff);
|
|
if (len != 0) {
|
|
memcpy(dst, src->data(), len);
|
|
dst += len;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'S': {
|
|
assert(len == 0);
|
|
const UInt32 len = va_arg(args, UInt32);
|
|
const UInt8* src = va_arg(args, UInt8*);
|
|
*dst++ = static_cast<UInt8>((len >> 24) & 0xff);
|
|
*dst++ = static_cast<UInt8>((len >> 16) & 0xff);
|
|
*dst++ = static_cast<UInt8>((len >> 8) & 0xff);
|
|
*dst++ = static_cast<UInt8>( len & 0xff);
|
|
memcpy(dst, src, len);
|
|
dst += len;
|
|
break;
|
|
}
|
|
|
|
case '%':
|
|
assert(len == 0);
|
|
*dst++ = '%';
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "invalid format specifier");
|
|
}
|
|
|
|
// next format character
|
|
++fmt;
|
|
}
|
|
else {
|
|
// copy regular character
|
|
*dst++ = *fmt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
UInt32
|
|
CProtocolUtil::eatLength(const char** pfmt)
|
|
{
|
|
const char* fmt = *pfmt;
|
|
UInt32 n = 0;
|
|
for (;;) {
|
|
UInt32 d;
|
|
switch (*fmt) {
|
|
case '0': d = 0; break;
|
|
case '1': d = 1; break;
|
|
case '2': d = 2; break;
|
|
case '3': d = 3; break;
|
|
case '4': d = 4; break;
|
|
case '5': d = 5; break;
|
|
case '6': d = 6; break;
|
|
case '7': d = 7; break;
|
|
case '8': d = 8; break;
|
|
case '9': d = 9; break;
|
|
default: *pfmt = fmt; return n;
|
|
}
|
|
n = 10 * n + d;
|
|
++fmt;
|
|
}
|
|
}
|
|
|
|
void
|
|
CProtocolUtil::read(IStream* stream, void* vbuffer, UInt32 count)
|
|
{
|
|
assert(stream != NULL);
|
|
assert(vbuffer != NULL);
|
|
|
|
UInt8* buffer = reinterpret_cast<UInt8*>(vbuffer);
|
|
while (count > 0) {
|
|
// read more
|
|
UInt32 n = stream->read(buffer, count);
|
|
|
|
// bail if stream has hungup
|
|
if (n == 0) {
|
|
LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count));
|
|
throw XIOEndOfStream();
|
|
}
|
|
|
|
// prepare for next read
|
|
buffer += n;
|
|
count -= n;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// XIOReadMismatch
|
|
//
|
|
|
|
CString
|
|
XIOReadMismatch::getWhat() const throw()
|
|
{
|
|
return format("XIOReadMismatch", "CProtocolUtil::readf() mismatch");
|
|
}
|