summaryrefslogtreecommitdiffstats
path: root/src/lib/barrier/ProtocolUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/barrier/ProtocolUtil.cpp')
-rw-r--r--src/lib/barrier/ProtocolUtil.cpp544
1 files changed, 544 insertions, 0 deletions
diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp
new file mode 100644
index 0000000..42fe69c
--- /dev/null
+++ b/src/lib/barrier/ProtocolUtil.cpp
@@ -0,0 +1,544 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "common/stdvector.h"
+
+#include <cctype>
+#include <cstring>
+
+//
+// ProtocolUtil
+//
+
+void
+ProtocolUtil::writef(barrier::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
+ProtocolUtil::readf(barrier::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
+ProtocolUtil::vwritef(barrier::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
+ 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;
+ }
+}
+
+void
+ProtocolUtil::vreadf(barrier::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
+ *static_cast<UInt8*>(v) = buffer[0];
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt8*>(v), *static_cast<UInt8*>(v)));
+ break;
+
+ case 2:
+ // 2 byte integer
+ *static_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, *static_cast<UInt16*>(v), *static_cast<UInt16*>(v)));
+ break;
+
+ case 4:
+ // 4 byte integer
+ *static_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, *static_cast<UInt32*>(v), *static_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);
+ static_cast<std::vector<UInt8>*>(v)->push_back(
+ buffer[0]);
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt8>*>(v)->back(), static_cast<std::vector<UInt8>*>(v)->back()));
+ }
+ break;
+
+ case 2:
+ // 2 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 2);
+ static_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, static_cast<std::vector<UInt16>*>(v)->back(), static_cast<std::vector<UInt16>*>(v)->back()));
+ }
+ break;
+
+ case 4:
+ // 4 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 4);
+ static_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, static_cast<std::vector<UInt32>*>(v)->back(), static_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 = buffer;
+ if (!useFixed) {
+ 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", len));
+
+ // save the data
+ String* dst = va_arg(args, String*);
+ 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
+ProtocolUtil::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 = (UInt32)(va_arg(args, std::vector<UInt8>*))->size() + 4;
+ break;
+
+ case 2:
+ len = 2 * (UInt32)(va_arg(args, std::vector<UInt16>*))->size() + 4;
+ break;
+
+ case 4:
+ len = 4 * (UInt32)(va_arg(args, std::vector<UInt32>*))->size() + 4;
+ break;
+ }
+ break;
+
+ case 's':
+ assert(len == 0);
+ len = (UInt32)(va_arg(args, String*))->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
+ProtocolUtil::writef(void* buffer, const char* fmt, va_list args)
+{
+ UInt8* dst = static_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 = (UInt32)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 = (UInt32)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 = (UInt32)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 String* src = va_arg(args, String*);
+ const UInt32 len = (src != NULL) ? (UInt32)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
+ProtocolUtil::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
+ProtocolUtil::read(barrier::IStream* stream, void* vbuffer, UInt32 count)
+{
+ assert(stream != NULL);
+ assert(vbuffer != NULL);
+
+ UInt8* buffer = static_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
+//
+
+String
+XIOReadMismatch::getWhat() const throw()
+{
+ return format("XIOReadMismatch", "ProtocolUtil::readf() mismatch");
+}