From a3c0ce55c90d6921aca55f2316ea98b06bbb951d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 18 May 2020 18:53:43 +0200 Subject: [PATCH 001/207] Support calling methods from timers We can't safely use the normal timers in base classes as we cannot guarantee that subclasses will call the base class' handleTimeout() properly if the subclass overrides it. --- common/rfb/Timer.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/rfb/Timer.h b/common/rfb/Timer.h index ef50972..ddfce1b 100644 --- a/common/rfb/Timer.h +++ b/common/rfb/Timer.h @@ -35,6 +35,8 @@ namespace rfb { dispatch elapsed Timer callbacks and to determine how long to wait in select() for the next timeout to occur. + For classes that can be derived it's best to use MethodTimer which can call a specific + method on the class, thus avoiding conflicts when subclassing. */ struct Timer { @@ -101,6 +103,19 @@ namespace rfb { static std::list pending; }; + template class MethodTimer + : public Timer, public Timer::Callback { + public: + MethodTimer(T* obj_, bool (T::*cb_)(Timer*)) + : Timer(this), obj(obj_), cb(cb_) {} + + virtual bool handleTimeout(Timer* t) { return (obj->*cb)(t); } + + private: + T* obj; + bool (T::*cb)(Timer*); + }; + }; #endif From c5b7137f2b1170cddf79bd79d39224aee4aba7d5 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 18 May 2020 19:40:49 +0200 Subject: [PATCH 002/207] Flush data on close There might be some final handshake data that is still stuck in the buffers, so make a best effort attempt at getting it to the client. --- common/rfb/VNCSConnectionST.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 31bd16e..9e7f3de 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -155,6 +155,17 @@ void VNCSConnectionST::close(const char* reason) server->lastDisconnectTime = time(0); } + try { + if (sock->outStream().bufferUsage() > 0) { + sock->cork(false); + sock->outStream().flush(); + if (sock->outStream().bufferUsage() > 0) + vlog.error("Failed to flush remaining socket data on close"); + } + } catch (rdr::Exception& e) { + vlog.error("Failed to flush remaining socket data on close: %s", e.str()); + } + // Just shutdown the socket and mark our state as closing. Eventually the // calling code will call VNCServerST's removeSocket() method causing us to // be deleted. From c97828471c59db4c2fd56a8f57d1b8cd95397e30 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 18 May 2020 19:41:40 +0200 Subject: [PATCH 003/207] Use proper constants for socket shutdown() For readability. --- common/network/Socket.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/network/Socket.cxx b/common/network/Socket.cxx index 9dd8bfe..78484f5 100644 --- a/common/network/Socket.cxx +++ b/common/network/Socket.cxx @@ -25,6 +25,9 @@ #include #include #define errorNumber WSAGetLastError() +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH #else #define errorNumber errno #define closesocket close @@ -94,7 +97,7 @@ Socket::~Socket() void Socket::shutdown() { isShutdown_ = true; - ::shutdown(getFd(), 2); + ::shutdown(getFd(), SHUT_RDWR); } bool Socket::isShutdown() const @@ -149,7 +152,7 @@ void SocketListener::shutdown() closesocket(fd); fd = -1; #else - ::shutdown(fd, 2); + ::shutdown(fd, SHUT_RDWR); #endif } From 910fd8fa3ea8f9c6549eb8e7bae6882e0b94c655 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 May 2020 19:23:56 +0200 Subject: [PATCH 004/207] Remove unused stream methods They were accidentally left unused in fbad8a9 so they haven't been used in some time. --- common/rdr/FdInStream.cxx | 29 +---------------------------- common/rdr/FdInStream.h | 1 - common/rdr/MemOutStream.h | 6 ------ 3 files changed, 1 insertion(+), 35 deletions(-) diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index 1730d6d..ceb95c9 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -53,8 +53,7 @@ using namespace rdr; -enum { DEFAULT_BUF_SIZE = 8192, - MIN_BULK_SIZE = 1024 }; +enum { DEFAULT_BUF_SIZE = 8192 }; FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_, bool closeWhenDone_) @@ -97,32 +96,6 @@ size_t FdInStream::pos() return offset + ptr - start; } -void FdInStream::readBytes(void* data, size_t length) -{ - if (length < MIN_BULK_SIZE) { - InStream::readBytes(data, length); - return; - } - - U8* dataPtr = (U8*)data; - - size_t n = end - ptr; - if (n > length) n = length; - - memcpy(dataPtr, ptr, n); - dataPtr += n; - length -= n; - ptr += n; - - while (length > 0) { - n = readWithTimeoutOrCallback(dataPtr, length); - dataPtr += n; - length -= n; - offset += n; - } -} - - size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait) { if (itemSize > bufSize) diff --git a/common/rdr/FdInStream.h b/common/rdr/FdInStream.h index d99ad3c..007f35c 100644 --- a/common/rdr/FdInStream.h +++ b/common/rdr/FdInStream.h @@ -47,7 +47,6 @@ namespace rdr { void setBlockCallback(FdInStreamBlockCallback* blockCallback); int getFd() { return fd; } size_t pos(); - void readBytes(void* data, size_t length); void startTiming(); void stopTiming(); diff --git a/common/rdr/MemOutStream.h b/common/rdr/MemOutStream.h index b56bac3..a839473 100644 --- a/common/rdr/MemOutStream.h +++ b/common/rdr/MemOutStream.h @@ -41,12 +41,6 @@ namespace rdr { delete [] start; } - void writeBytes(const void* data, size_t length) { - check(length); - memcpy(ptr, data, length); - ptr += length; - } - size_t length() { return ptr - start; } void clear() { ptr = start; }; void clearAndZero() { memset(start, 0, ptr-start); clear(); } From 7f90205cf2f06a461b1642f8a6aca63f234e659c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 19 May 2020 20:45:22 +0200 Subject: [PATCH 005/207] Add stream avail() methods Makes it more readable to write code that needs to know how much data/space is available in a stream. --- common/rdr/FdInStream.cxx | 2 +- common/rdr/FdOutStream.cxx | 4 ++-- common/rdr/FileInStream.cxx | 2 +- common/rdr/HexInStream.cxx | 4 ++-- common/rdr/HexOutStream.cxx | 2 +- common/rdr/InStream.h | 12 ++++++++++-- common/rdr/OutStream.h | 12 ++++++++++-- common/rdr/RandomStream.cxx | 2 +- common/rdr/TLSInStream.cxx | 2 +- common/rdr/TLSOutStream.cxx | 2 +- common/rdr/ZlibInStream.cxx | 6 +++--- common/rdr/ZlibOutStream.cxx | 6 +++--- common/rfb/JpegCompressor.cxx | 4 ++-- 13 files changed, 38 insertions(+), 22 deletions(-) diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index ceb95c9..eb76ec0 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -126,7 +126,7 @@ size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait) } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index f5d07e4..04c6851 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -129,7 +129,7 @@ size_t FdOutStream::overrun(size_t itemSize, size_t nItems) flush(); // Still not enough space? - if (itemSize > (size_t)(end - ptr)) { + if (itemSize > avail()) { // Can we shuffle things around? // (don't do this if it gains us less than 25%) if (((size_t)(sentUpTo - start) > bufSize / 4) && @@ -150,7 +150,7 @@ size_t FdOutStream::overrun(size_t itemSize, size_t nItems) } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/FileInStream.cxx b/common/rdr/FileInStream.cxx index bdb05a3..8344fcd 100644 --- a/common/rdr/FileInStream.cxx +++ b/common/rdr/FileInStream.cxx @@ -81,7 +81,7 @@ size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait) } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx index a6bc92c..787edc0 100644 --- a/common/rdr/HexInStream.cxx +++ b/common/rdr/HexInStream.cxx @@ -91,7 +91,7 @@ size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) { offset += ptr - start; ptr = start; - while ((size_t)(end - ptr) < itemSize) { + while (avail() < itemSize) { size_t n = in_stream.check(2, 1, wait); if (n == 0) return 0; const U8* iptr = in_stream.getptr(); @@ -111,7 +111,7 @@ size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) { } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/HexOutStream.cxx b/common/rdr/HexOutStream.cxx index eac2eff..6118c13 100644 --- a/common/rdr/HexOutStream.cxx +++ b/common/rdr/HexOutStream.cxx @@ -103,7 +103,7 @@ HexOutStream::overrun(size_t itemSize, size_t nItems) { writeBuffer(); size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h index a8e515d..f6b6df5 100644 --- a/common/rdr/InStream.h +++ b/common/rdr/InStream.h @@ -35,6 +35,14 @@ namespace rdr { virtual ~InStream() {} + // avail() returns the number of bytes that are currenctly directly + // available from the stream. + + inline size_t avail() + { + return end - ptr; + } + // check() ensures there is buffer data for at least one item of size // itemSize bytes. Returns the number of items in the buffer (up to a // maximum of nItems). If wait is false, then instead of blocking to wait @@ -48,11 +56,11 @@ namespace rdr { if (itemSize == 0 || nItems == 0) return 0; - if (itemSize > (size_t)(end - ptr)) + if (itemSize > avail()) return overrun(itemSize, nItems, wait); // itemSize cannot be zero at this point - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/OutStream.h b/common/rdr/OutStream.h index 0f60ccc..d7b3a6a 100644 --- a/common/rdr/OutStream.h +++ b/common/rdr/OutStream.h @@ -40,6 +40,14 @@ namespace rdr { virtual ~OutStream() {} + // avail() returns the number of bytes that currently be written to the + // stream without any risk of blocking. + + inline size_t avail() + { + return end - ptr; + } + // check() ensures there is buffer space for at least one item of size // itemSize bytes. Returns the number of items which fit (up to a maximum // of nItems). @@ -48,10 +56,10 @@ namespace rdr { { size_t nAvail; - if (itemSize > (size_t)(end - ptr)) + if (itemSize > avail()) return overrun(itemSize, nItems); - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/RandomStream.cxx b/common/rdr/RandomStream.cxx index 6c64ac5..50abbde 100644 --- a/common/rdr/RandomStream.cxx +++ b/common/rdr/RandomStream.cxx @@ -124,7 +124,7 @@ size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) { } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx index cd81f22..15e2a47 100644 --- a/common/rdr/TLSInStream.cxx +++ b/common/rdr/TLSInStream.cxx @@ -100,7 +100,7 @@ size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait) } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx index 7d7c3b5..089aa66 100644 --- a/common/rdr/TLSOutStream.cxx +++ b/common/rdr/TLSOutStream.cxx @@ -101,7 +101,7 @@ size_t TLSOutStream::overrun(size_t itemSize, size_t nItems) flush(); size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; diff --git a/common/rdr/ZlibInStream.cxx b/common/rdr/ZlibInStream.cxx index 0fb3ad1..839cf0d 100644 --- a/common/rdr/ZlibInStream.cxx +++ b/common/rdr/ZlibInStream.cxx @@ -108,13 +108,13 @@ size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait) end -= ptr - start; ptr = start; - while ((size_t)(end - ptr) < itemSize) { + while (avail() < itemSize) { if (!decompress(wait)) return 0; } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; @@ -136,7 +136,7 @@ bool ZlibInStream::decompress(bool wait) size_t n = underlying->check(1, 1, wait); if (n == 0) return false; zs->next_in = (U8*)underlying->getptr(); - zs->avail_in = underlying->getend() - underlying->getptr(); + zs->avail_in = underlying->avail(); if (zs->avail_in > bytesIn) zs->avail_in = bytesIn; diff --git a/common/rdr/ZlibOutStream.cxx b/common/rdr/ZlibOutStream.cxx index 1cccb2b..fac08bd 100644 --- a/common/rdr/ZlibOutStream.cxx +++ b/common/rdr/ZlibOutStream.cxx @@ -106,7 +106,7 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems) checkCompressionLevel(); - while ((size_t)(end - ptr) < itemSize) { + while (avail() < itemSize) { zs->next_in = start; zs->avail_in = ptr - start; @@ -128,7 +128,7 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems) } size_t nAvail; - nAvail = (end - ptr) / itemSize; + nAvail = avail() / itemSize; if (nAvail < nItems) return nAvail; @@ -148,7 +148,7 @@ void ZlibOutStream::deflate(int flush) do { underlying->check(1); zs->next_out = underlying->getptr(); - zs->avail_out = underlying->getend() - underlying->getptr(); + zs->avail_out = underlying->avail(); #ifdef ZLIBOUT_DEBUG fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n", diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx index 27cb9de..9a5f8d2 100644 --- a/common/rfb/JpegCompressor.cxx +++ b/common/rfb/JpegCompressor.cxx @@ -85,7 +85,7 @@ JpegInitDestination(j_compress_ptr cinfo) jc->clear(); dest->pub.next_output_byte = jc->getptr(); - dest->pub.free_in_buffer = jc->getend() - jc->getptr(); + dest->pub.free_in_buffer = jc->avail(); } static boolean @@ -97,7 +97,7 @@ JpegEmptyOutputBuffer(j_compress_ptr cinfo) jc->setptr(jc->getend()); jc->overrun(jc->getend() - jc->getstart(), 1); dest->pub.next_output_byte = jc->getptr(); - dest->pub.free_in_buffer = jc->getend() - jc->getptr(); + dest->pub.free_in_buffer = jc->avail(); return TRUE; } From 92c76959818dfc56ce35855e20858437c2e2c0ca Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 May 2020 20:20:53 +0200 Subject: [PATCH 006/207] Create common base classes for buffered streams Most streams are backed by a memory buffer. Create common base classes for this functionality to avoid code duplication. --- common/rdr/BufferedInStream.cxx | 72 +++++++++++++++++++ common/rdr/BufferedInStream.h | 54 +++++++++++++++ common/rdr/BufferedOutStream.cxx | 115 +++++++++++++++++++++++++++++++ common/rdr/BufferedOutStream.h | 65 +++++++++++++++++ common/rdr/CMakeLists.txt | 2 + common/rdr/FdInStream.cxx | 67 ++++-------------- common/rdr/FdInStream.h | 11 ++- common/rdr/FdOutStream.cxx | 91 +++++------------------- common/rdr/FdOutStream.h | 15 +--- common/rdr/FileInStream.cxx | 54 +++------------ common/rdr/FileInStream.h | 13 ++-- common/rdr/HexInStream.cxx | 56 +++++---------- common/rdr/HexInStream.h | 14 ++-- common/rdr/RandomStream.cxx | 44 +++--------- common/rdr/RandomStream.h | 13 ++-- common/rdr/TLSInStream.cxx | 46 +++---------- common/rdr/TLSInStream.h | 11 +-- common/rdr/ZlibInStream.cxx | 55 +++------------ common/rdr/ZlibInStream.h | 14 ++-- 19 files changed, 414 insertions(+), 398 deletions(-) create mode 100644 common/rdr/BufferedInStream.cxx create mode 100644 common/rdr/BufferedInStream.h create mode 100644 common/rdr/BufferedOutStream.cxx create mode 100644 common/rdr/BufferedOutStream.h diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx new file mode 100644 index 0000000..5083eb2 --- /dev/null +++ b/common/rdr/BufferedInStream.cxx @@ -0,0 +1,72 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2020 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +using namespace rdr; + +static const size_t DEFAULT_BUF_SIZE = 8192; + +BufferedInStream::BufferedInStream(size_t bufSize_) + : bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + ptr = end = start = new U8[bufSize]; +} + +BufferedInStream::~BufferedInStream() +{ + delete [] start; +} + +size_t BufferedInStream::pos() +{ + return offset + ptr - start; +} + +size_t BufferedInStream::overrun(size_t itemSize, size_t nItems, bool wait) +{ + if (itemSize > bufSize) + throw Exception("BufferedInStream overrun: " + "requested size of %lu bytes exceeds maximum of %lu bytes", + (long unsigned)itemSize, (long unsigned)bufSize); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + offset += ptr - start; + end -= ptr - start; + ptr = start; + + while (avail() < itemSize) { + if (!fillBuffer(start + bufSize - end, wait)) + return 0; + } + + size_t nAvail; + nAvail = avail() / itemSize; + if (nAvail < nItems) + return nAvail; + + return nItems; +} diff --git a/common/rdr/BufferedInStream.h b/common/rdr/BufferedInStream.h new file mode 100644 index 0000000..fc62133 --- /dev/null +++ b/common/rdr/BufferedInStream.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2020 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// Base class for input streams with a buffer +// + +#ifndef __RDR_BUFFEREDINSTREAM_H__ +#define __RDR_BUFFEREDINSTREAM_H__ + +#include + +namespace rdr { + + class BufferedInStream : public InStream { + + public: + virtual ~BufferedInStream(); + + virtual size_t pos(); + + private: + virtual bool fillBuffer(size_t maxSize, bool wait) = 0; + + virtual size_t overrun(size_t itemSize, size_t nItems, bool wait); + + private: + size_t bufSize; + size_t offset; + U8* start; + + protected: + BufferedInStream(size_t bufSize=0); + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/BufferedOutStream.cxx b/common/rdr/BufferedOutStream.cxx new file mode 100644 index 0000000..76b0163 --- /dev/null +++ b/common/rdr/BufferedOutStream.cxx @@ -0,0 +1,115 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2020 Pierre Ossman for Cendio AB + * Copyright 2017 Peter Astrand for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +using namespace rdr; + +static const size_t DEFAULT_BUF_SIZE = 16384; + +BufferedOutStream::BufferedOutStream(size_t bufSize_) + : bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + ptr = start = sentUpTo = new U8[bufSize]; + end = start + bufSize; +} + +BufferedOutStream::~BufferedOutStream() +{ + // FIXME: Complain about non-flushed buffer? + delete [] start; +} + +size_t BufferedOutStream::length() +{ + return offset + ptr - sentUpTo; +} + +size_t BufferedOutStream::bufferUsage() +{ + return ptr - sentUpTo; +} + +void BufferedOutStream::flush() +{ + while (sentUpTo < ptr) { + size_t len; + + len = bufferUsage(); + + if (!flushBuffer(false)) + break; + + offset += len - bufferUsage(); + } + + // Managed to flush everything? + if (sentUpTo == ptr) + ptr = sentUpTo = start; +} + +size_t BufferedOutStream::overrun(size_t itemSize, size_t nItems) +{ + if (itemSize > bufSize) + throw Exception("BufferedOutStream overrun: " + "requested size of %lu bytes exceeds maximum of %lu bytes", + (long unsigned)itemSize, (long unsigned)bufSize); + + // First try to get rid of the data we have + flush(); + + // Still not enough space? + while (itemSize > avail()) { + // Can we shuffle things around? + // (don't do this if it gains us less than 25%) + if (((size_t)(sentUpTo - start) > bufSize / 4) && + (itemSize < bufSize - (ptr - sentUpTo))) { + memmove(start, sentUpTo, ptr - sentUpTo); + ptr = start + (ptr - sentUpTo); + sentUpTo = start; + } else { + size_t len; + + len = bufferUsage(); + + // Have to get rid of more data, so allow the flush to wait... + flushBuffer(true); + + offset += len - bufferUsage(); + + // Managed to flush everything? + if (sentUpTo == ptr) + ptr = sentUpTo = start; + } + } + + size_t nAvail; + nAvail = avail() / itemSize; + if (nAvail < nItems) + return nAvail; + + return nItems; +} diff --git a/common/rdr/BufferedOutStream.h b/common/rdr/BufferedOutStream.h new file mode 100644 index 0000000..092ea1f --- /dev/null +++ b/common/rdr/BufferedOutStream.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2020 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// Base class for output streams with a buffer +// + +#ifndef __RDR_BUFFEREDOUTSTREAM_H__ +#define __RDR_BUFFEREDOUTSTREAM_H__ + +#include + +namespace rdr { + + class BufferedOutStream : public OutStream { + + public: + virtual ~BufferedOutStream(); + + virtual size_t length(); + virtual void flush(); + + size_t bufferUsage(); + + private: + // flushBuffer() requests that the stream be flushed. Returns true if it is + // able to progress the output (which might still not mean any bytes + // actually moved) and can be called again. If wait is true then it will + // block until all data has been written. + + virtual bool flushBuffer(bool wait) = 0; + + virtual size_t overrun(size_t itemSize, size_t nItems); + + private: + size_t bufSize; + size_t offset; + U8* start; + + protected: + U8* sentUpTo; + + protected: + BufferedOutStream(size_t bufSize=0); + }; + +} + +#endif diff --git a/common/rdr/CMakeLists.txt b/common/rdr/CMakeLists.txt index 989ba2f..78778dd 100644 --- a/common/rdr/CMakeLists.txt +++ b/common/rdr/CMakeLists.txt @@ -1,6 +1,8 @@ include_directories(${CMAKE_SOURCE_DIR}/common ${ZLIB_INCLUDE_DIRS}) add_library(rdr STATIC + BufferedInStream.cxx + BufferedOutStream.cxx Exception.cxx FdInStream.cxx FdOutStream.cxx diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index eb76ec0..c9d2241 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -36,13 +36,6 @@ #include #endif -#ifndef vncmin -#define vncmin(a,b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef vncmax -#define vncmax(a,b) (((a) > (b)) ? (a) : (b)) -#endif - /* Old systems have select() in sys/time.h */ #ifdef HAVE_SYS_SELECT_H #include @@ -57,26 +50,23 @@ enum { DEFAULT_BUF_SIZE = 8192 }; FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_, bool closeWhenDone_) - : fd(fd_), closeWhenDone(closeWhenDone_), + : BufferedInStream(bufSize_), + fd(fd_), closeWhenDone(closeWhenDone_), timeoutms(timeoutms_), blockCallback(0), - timing(false), timeWaitedIn100us(5), timedKbits(0), - bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) + timing(false), timeWaitedIn100us(5), timedKbits(0) { - ptr = end = start = new U8[bufSize]; } FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_, size_t bufSize_) - : fd(fd_), timeoutms(0), blockCallback(blockCallback_), - timing(false), timeWaitedIn100us(5), timedKbits(0), - bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) + : BufferedInStream(bufSize_), + fd(fd_), timeoutms(0), blockCallback(blockCallback_), + timing(false), timeWaitedIn100us(5), timedKbits(0) { - ptr = end = start = new U8[bufSize]; } FdInStream::~FdInStream() { - delete [] start; if (closeWhenDone) close(fd); } @@ -91,46 +81,15 @@ void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_) timeoutms = 0; } -size_t FdInStream::pos() + +bool FdInStream::fillBuffer(size_t maxSize, bool wait) { - return offset + ptr - start; -} + size_t n = readWithTimeoutOrCallback((U8*)end, maxSize, wait); + if (n == 0) + return false; + end += n; -size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait) -{ - if (itemSize > bufSize) - throw Exception("FdInStream overrun: max itemSize exceeded"); - - if (end - ptr != 0) - memmove(start, ptr, end - ptr); - - offset += ptr - start; - end -= ptr - start; - ptr = start; - - size_t bytes_to_read; - while ((size_t)(end - start) < itemSize) { - bytes_to_read = start + bufSize - end; - if (!timing) { - // When not timing, we must be careful not to read too much - // extra data into the buffer. Otherwise, the line speed - // estimation might stay at zero for a long time: All reads - // during timing=1 can be satisfied without calling - // readWithTimeoutOrCallback. However, reading only 1 or 2 bytes - // bytes is ineffecient. - bytes_to_read = vncmin(bytes_to_read, vncmax(itemSize*nItems, 8)); - } - size_t n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait); - if (n == 0) return 0; - end += n; - } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } // diff --git a/common/rdr/FdInStream.h b/common/rdr/FdInStream.h index 007f35c..f7a52ba 100644 --- a/common/rdr/FdInStream.h +++ b/common/rdr/FdInStream.h @@ -23,7 +23,7 @@ #ifndef __RDR_FDINSTREAM_H__ #define __RDR_FDINSTREAM_H__ -#include +#include namespace rdr { @@ -33,7 +33,7 @@ namespace rdr { virtual ~FdInStreamBlockCallback() {} }; - class FdInStream : public InStream { + class FdInStream : public BufferedInStream { public: @@ -46,17 +46,15 @@ namespace rdr { void setTimeout(int timeoutms); void setBlockCallback(FdInStreamBlockCallback* blockCallback); int getFd() { return fd; } - size_t pos(); void startTiming(); void stopTiming(); unsigned int kbitsPerSecond(); unsigned int timeWaited() { return timeWaitedIn100us; } - protected: - size_t overrun(size_t itemSize, size_t nItems, bool wait); - private: + virtual bool fillBuffer(size_t maxSize, bool wait); + size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true); int fd; @@ -68,7 +66,6 @@ namespace rdr { unsigned int timeWaitedIn100us; unsigned int timedKbits; - size_t bufSize; size_t offset; U8* start; }; diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index 04c6851..4fc7467 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -49,26 +49,20 @@ using namespace rdr; -enum { DEFAULT_BUF_SIZE = 16384 }; - FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, size_t bufSize_) - : fd(fd_), blocking(blocking_), timeoutms(timeoutms_), - bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) + : BufferedOutStream(bufSize_), + fd(fd_), blocking(blocking_), timeoutms(timeoutms_) { - ptr = start = sentUpTo = new U8[bufSize]; - end = start + bufSize; - gettimeofday(&lastWrite, NULL); } FdOutStream::~FdOutStream() { try { - blocking = true; - flush(); + while (sentUpTo != ptr) + flushBuffer(true); } catch (Exception&) { } - delete [] start; } void FdOutStream::setTimeout(int timeoutms_) { @@ -79,82 +73,29 @@ void FdOutStream::setBlocking(bool blocking_) { blocking = blocking_; } -size_t FdOutStream::length() -{ - return offset + ptr - sentUpTo; -} - -int FdOutStream::bufferUsage() -{ - return ptr - sentUpTo; -} - unsigned FdOutStream::getIdleTime() { return rfb::msSince(&lastWrite); } -void FdOutStream::flush() +bool FdOutStream::flushBuffer(bool wait) { - while (sentUpTo < ptr) { - size_t n = writeWithTimeout((const void*) sentUpTo, - ptr - sentUpTo, - blocking? timeoutms : 0); + size_t n = writeWithTimeout((const void*) sentUpTo, + ptr - sentUpTo, + (blocking || wait)? timeoutms : 0); - // Timeout? - if (n == 0) { - // If non-blocking then we're done here - if (!blocking) - break; + // Timeout? + if (n == 0) { + // If non-blocking then we're done here + if (!blocking && !wait) + return false; - throw TimedOut(); - } - - sentUpTo += n; - offset += n; + throw TimedOut(); } - // Managed to flush everything? - if (sentUpTo == ptr) - ptr = sentUpTo = start; -} + sentUpTo += n; - -size_t FdOutStream::overrun(size_t itemSize, size_t nItems) -{ - if (itemSize > bufSize) - throw Exception("FdOutStream overrun: max itemSize exceeded"); - - // First try to get rid of the data we have - flush(); - - // Still not enough space? - if (itemSize > avail()) { - // Can we shuffle things around? - // (don't do this if it gains us less than 25%) - if (((size_t)(sentUpTo - start) > bufSize / 4) && - (itemSize < bufSize - (ptr - sentUpTo))) { - memmove(start, sentUpTo, ptr - sentUpTo); - ptr = start + (ptr - sentUpTo); - sentUpTo = start; - } else { - // Have to get rid of more data, so turn off non-blocking - // for a bit... - bool realBlocking; - - realBlocking = blocking; - blocking = true; - flush(); - blocking = realBlocking; - } - } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } // diff --git a/common/rdr/FdOutStream.h b/common/rdr/FdOutStream.h index ed84fdb..b1ecbd5 100644 --- a/common/rdr/FdOutStream.h +++ b/common/rdr/FdOutStream.h @@ -26,11 +26,11 @@ #include -#include +#include namespace rdr { - class FdOutStream : public OutStream { + class FdOutStream : public BufferedOutStream { public: @@ -41,23 +41,14 @@ namespace rdr { void setBlocking(bool blocking); int getFd() { return fd; } - void flush(); - size_t length(); - - int bufferUsage(); - unsigned getIdleTime(); private: - size_t overrun(size_t itemSize, size_t nItems); + virtual bool flushBuffer(bool wait); size_t writeWithTimeout(const void* data, size_t length, int timeoutms); int fd; bool blocking; int timeoutms; - size_t bufSize; - size_t offset; - U8* start; - U8* sentUpTo; struct timeval lastWrite; }; diff --git a/common/rdr/FileInStream.cxx b/common/rdr/FileInStream.cxx index 8344fcd..66dfe76 100644 --- a/common/rdr/FileInStream.cxx +++ b/common/rdr/FileInStream.cxx @@ -30,7 +30,6 @@ FileInStream::FileInStream(const char *fileName) file = fopen(fileName, "rb"); if (!file) throw SystemException("fopen", errno); - ptr = end = b; } FileInStream::~FileInStream(void) { @@ -40,50 +39,17 @@ FileInStream::~FileInStream(void) { } } -void FileInStream::reset(void) { - if (!file) - throw Exception("File is not open"); - if (fseek(file, 0, SEEK_SET) != 0) - throw SystemException("fseek", errno); - ptr = end = b; -} - -size_t FileInStream::pos() +bool FileInStream::fillBuffer(size_t maxSize, bool wait) { - if (!file) - throw Exception("File is not open"); - - return ftell(file) + ptr - b; -} - -size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait) -{ - if (itemSize > sizeof(b)) - throw Exception("FileInStream overrun: max itemSize exceeded"); - - if (end - ptr != 0) - memmove(b, ptr, end - ptr); - - end -= ptr - b; - ptr = b; - - - while ((size_t)(end - b) < itemSize) { - size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file); - if (n == 0) { - if (ferror(file)) - throw SystemException("fread", errno); - if (feof(file)) - throw EndOfStream(); - return 0; - } - end += b + sizeof(b) - end; + size_t n = fread((U8 *)end, 1, maxSize, file); + if (n == 0) { + if (ferror(file)) + throw SystemException("fread", errno); + if (feof(file)) + throw EndOfStream(); + return false; } + end += n; - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } diff --git a/common/rdr/FileInStream.h b/common/rdr/FileInStream.h index a33c765..268f537 100644 --- a/common/rdr/FileInStream.h +++ b/common/rdr/FileInStream.h @@ -22,26 +22,21 @@ #include -#include +#include namespace rdr { - class FileInStream : public InStream { + class FileInStream : public BufferedInStream { public: FileInStream(const char *fileName); ~FileInStream(void); - void reset(void); - - size_t pos(); - - protected: - size_t overrun(size_t itemSize, size_t nItems, bool wait = true); + private: + virtual bool fillBuffer(size_t maxSize, bool wait); private: - U8 b[131072]; FILE *file; }; diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx index 787edc0..0901afe 100644 --- a/common/rdr/HexInStream.cxx +++ b/common/rdr/HexInStream.cxx @@ -24,18 +24,14 @@ using namespace rdr; -const int DEFAULT_BUF_LEN = 16384; - static inline int min(int a, int b) {return a bufSize) - throw Exception("HexInStream overrun: max itemSize exceeded"); + const U8* iptr = in_stream.getptr(); + const U8* eptr = in_stream.getend(); + size_t length = min((eptr - iptr)/2, maxSize); - if (end - ptr != 0) - memmove(start, ptr, end - ptr); - - end -= ptr - start; - offset += ptr - start; - ptr = start; - - while (avail() < itemSize) { - size_t n = in_stream.check(2, 1, wait); - if (n == 0) return 0; - const U8* iptr = in_stream.getptr(); - const U8* eptr = in_stream.getend(); - size_t length = min((eptr - iptr)/2, start + bufSize - end); - - U8* optr = (U8*) end; - for (size_t i=0; i +#include namespace rdr { - class HexInStream : public InStream { + class HexInStream : public BufferedInStream { public: HexInStream(InStream& is, size_t bufSize=0); virtual ~HexInStream(); - size_t pos(); - static bool readHexAndShift(char c, int* v); static bool hexStrToBin(const char* s, char** data, size_t* length); - protected: - size_t overrun(size_t itemSize, size_t nItems, bool wait); + private: + virtual bool fillBuffer(size_t maxSize, bool wait); private: - size_t bufSize; - U8* start; - size_t offset; - InStream& in_stream; }; diff --git a/common/rdr/RandomStream.cxx b/common/rdr/RandomStream.cxx index 50abbde..27b33ad 100644 --- a/common/rdr/RandomStream.cxx +++ b/common/rdr/RandomStream.cxx @@ -32,15 +32,10 @@ using namespace rdr; -const size_t DEFAULT_BUF_LEN = 256; - unsigned int RandomStream::seed; RandomStream::RandomStream() - : offset(0) { - ptr = end = start = new U8[DEFAULT_BUF_LEN]; - #ifdef RFB_HAVE_WINCRYPT provider = 0; if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) { @@ -72,8 +67,6 @@ RandomStream::RandomStream() } RandomStream::~RandomStream() { - delete [] start; - #ifdef RFB_HAVE_WINCRYPT if (provider) CryptReleaseContext(provider, 0); @@ -83,50 +76,29 @@ RandomStream::~RandomStream() { #endif } -size_t RandomStream::pos() { - return offset + ptr - start; -} - -size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) { - if (itemSize > DEFAULT_BUF_LEN) - throw Exception("RandomStream overrun: max itemSize exceeded"); - - if (end - ptr != 0) - memmove(start, ptr, end - ptr); - - end -= ptr - start; - offset += ptr - start; - ptr = start; - - size_t length = start + DEFAULT_BUF_LEN - end; - +bool RandomStream::fillBuffer(size_t maxSize, bool wait) { #ifdef RFB_HAVE_WINCRYPT if (provider) { - if (!CryptGenRandom(provider, length, (U8*)end)) + if (!CryptGenRandom(provider, maxSize, (U8*)end)) throw rdr::SystemException("unable to CryptGenRandom", GetLastError()); - end += length; + end += maxSize; } else { #else #ifndef WIN32 if (fp) { - size_t n = fread((U8*)end, length, 1, fp); - if (n != 1) + size_t n = fread((U8*)end, 1, maxSize, fp); + if (n <= 0) throw rdr::SystemException("reading /dev/urandom or /dev/random failed", errno); - end += length; + end += n; } else { #else { #endif #endif - for (size_t i=0; i -#include +#include #ifdef WIN32 #include @@ -32,22 +32,17 @@ namespace rdr { - class RandomStream : public InStream { + class RandomStream : public BufferedInStream { public: RandomStream(); virtual ~RandomStream(); - size_t pos(); - - protected: - size_t overrun(size_t itemSize, size_t nItems, bool wait); + private: + virtual bool fillBuffer(size_t maxSize, bool wait); private: - U8* start; - size_t offset; - static unsigned int seed; #ifdef RFB_HAVE_WINCRYPT HCRYPTPROV provider; diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx index 15e2a47..92c2f0c 100644 --- a/common/rdr/TLSInStream.cxx +++ b/common/rdr/TLSInStream.cxx @@ -30,8 +30,6 @@ #ifdef HAVE_GNUTLS using namespace rdr; -enum { DEFAULT_BUF_SIZE = 16384 }; - ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) { TLSInStream* self= (TLSInStream*) str; @@ -43,8 +41,8 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) return -1; } - if ((size_t)(in->getend() - in->getptr()) < size) - size = in->getend() - in->getptr(); + if (in->avail() < size) + size = in->avail(); in->readBytes(data, size); @@ -57,12 +55,10 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) } TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session) - : session(_session), in(_in), bufSize(DEFAULT_BUF_SIZE), offset(0) + : session(_session), in(_in) { gnutls_transport_ptr_t recv, send; - ptr = end = start = new U8[bufSize]; - gnutls_transport_set_pull_function(session, pull); gnutls_transport_get_ptr2(session, &recv, &send); gnutls_transport_set_ptr2(session, this, send); @@ -71,40 +67,16 @@ TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session) TLSInStream::~TLSInStream() { gnutls_transport_set_pull_function(session, NULL); - - delete[] start; } -size_t TLSInStream::pos() +bool TLSInStream::fillBuffer(size_t maxSize, bool wait) { - return offset + ptr - start; -} + size_t n = readTLS((U8*) end, maxSize, wait); + if (!wait && n == 0) + return false; + end += n; -size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait) -{ - if (itemSize > bufSize) - throw Exception("TLSInStream overrun: max itemSize exceeded"); - - if (end - ptr != 0) - memmove(start, ptr, end - ptr); - - offset += ptr - start; - end -= ptr - start; - ptr = start; - - while ((size_t)(end - start) < itemSize) { - size_t n = readTLS((U8*) end, start + bufSize - end, wait); - if (!wait && n == 0) - return 0; - end += n; - } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait) diff --git a/common/rdr/TLSInStream.h b/common/rdr/TLSInStream.h index 5f9dee7..9779c68 100644 --- a/common/rdr/TLSInStream.h +++ b/common/rdr/TLSInStream.h @@ -27,27 +27,22 @@ #ifdef HAVE_GNUTLS #include -#include +#include namespace rdr { - class TLSInStream : public InStream { + class TLSInStream : public BufferedInStream { public: TLSInStream(InStream* in, gnutls_session_t session); virtual ~TLSInStream(); - size_t pos(); - private: - size_t overrun(size_t itemSize, size_t nItems, bool wait); + virtual bool fillBuffer(size_t maxSize, bool wait); size_t readTLS(U8* buf, size_t len, bool wait); static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size); gnutls_session_t session; InStream* in; - size_t bufSize; - size_t offset; - U8* start; }; }; diff --git a/common/rdr/ZlibInStream.cxx b/common/rdr/ZlibInStream.cxx index 839cf0d..675600d 100644 --- a/common/rdr/ZlibInStream.cxx +++ b/common/rdr/ZlibInStream.cxx @@ -24,41 +24,31 @@ using namespace rdr; -enum { DEFAULT_BUF_SIZE = 16384 }; - ZlibInStream::ZlibInStream(size_t bufSize_) - : underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0), - zs(NULL), bytesIn(0) + : BufferedInStream(bufSize_), + underlying(0), zs(NULL), bytesIn(0) { - ptr = end = start = new U8[bufSize]; init(); } ZlibInStream::~ZlibInStream() { deinit(); - delete [] start; } void ZlibInStream::setUnderlying(InStream* is, size_t bytesIn_) { underlying = is; bytesIn = bytesIn_; - ptr = end = start; -} - -size_t ZlibInStream::pos() -{ - return offset + ptr - start; + skip(avail()); } void ZlibInStream::flushUnderlying() { - ptr = end = start; - while (bytesIn > 0) { - decompress(true); - end = start; // throw away any data + if (!check(1)) + throw Exception("ZlibInStream: failed to flush remaining stream data"); + skip(avail()); } setUnderlying(NULL, 0); @@ -96,42 +86,13 @@ void ZlibInStream::deinit() zs = NULL; } -size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait) -{ - if (itemSize > bufSize) - throw Exception("ZlibInStream overrun: max itemSize exceeded"); - - if (end - ptr != 0) - memmove(start, ptr, end - ptr); - - offset += ptr - start; - end -= ptr - start; - ptr = start; - - while (avail() < itemSize) { - if (!decompress(wait)) - return 0; - } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; -} - -// decompress() calls the decompressor once. Note that this won't necessarily -// generate any output data - it may just consume some input data. Returns -// false if wait is false and we would block on the underlying stream. - -bool ZlibInStream::decompress(bool wait) +bool ZlibInStream::fillBuffer(size_t maxSize, bool wait) { if (!underlying) throw Exception("ZlibInStream overrun: no underlying stream"); zs->next_out = (U8*)end; - zs->avail_out = start + bufSize - end; + zs->avail_out = maxSize; size_t n = underlying->check(1, 1, wait); if (n == 0) return false; diff --git a/common/rdr/ZlibInStream.h b/common/rdr/ZlibInStream.h index 08784b0..0441675 100644 --- a/common/rdr/ZlibInStream.h +++ b/common/rdr/ZlibInStream.h @@ -24,38 +24,32 @@ #ifndef __RDR_ZLIBINSTREAM_H__ #define __RDR_ZLIBINSTREAM_H__ -#include +#include struct z_stream_s; namespace rdr { - class ZlibInStream : public InStream { + class ZlibInStream : public BufferedInStream { public: - ZlibInStream(size_t bufSize=0); virtual ~ZlibInStream(); void setUnderlying(InStream* is, size_t bytesIn); void flushUnderlying(); - size_t pos(); void reset(); private: - void init(); void deinit(); - size_t overrun(size_t itemSize, size_t nItems, bool wait); - bool decompress(bool wait); + virtual bool fillBuffer(size_t maxSize, bool wait); + private: InStream* underlying; - size_t bufSize; - size_t offset; z_stream_s* zs; size_t bytesIn; - U8* start; }; } // end of namespace rdr From 57a3c3bba82c44b51614132b8311d971195191b9 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 19 May 2020 21:07:05 +0200 Subject: [PATCH 007/207] Simplify stream availability handling Just have a simply number of bytes argument to avoid a lot of complexity. --- common/rdr/BufferedInStream.cxx | 17 +++++-------- common/rdr/BufferedInStream.h | 2 +- common/rdr/BufferedOutStream.cxx | 17 ++++--------- common/rdr/BufferedOutStream.h | 2 +- common/rdr/HexInStream.cxx | 2 +- common/rdr/HexOutStream.cxx | 14 +++-------- common/rdr/HexOutStream.h | 2 +- common/rdr/InStream.h | 43 +++++++++++--------------------- common/rdr/MemInStream.h | 2 +- common/rdr/MemOutStream.h | 10 +++----- common/rdr/OutStream.h | 42 +++++++++++-------------------- common/rdr/TLSInStream.cxx | 4 +-- common/rdr/TLSOutStream.cxx | 13 +++------- common/rdr/TLSOutStream.h | 2 +- common/rdr/ZlibInStream.cxx | 2 +- common/rdr/ZlibOutStream.cxx | 15 +++-------- common/rdr/ZlibOutStream.h | 2 +- common/rfb/CMsgWriter.cxx | 2 +- common/rfb/JpegCompressor.cxx | 2 +- common/rfb/JpegCompressor.h | 4 +-- 20 files changed, 67 insertions(+), 132 deletions(-) diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx index 5083eb2..5db3953 100644 --- a/common/rdr/BufferedInStream.cxx +++ b/common/rdr/BufferedInStream.cxx @@ -44,12 +44,12 @@ size_t BufferedInStream::pos() return offset + ptr - start; } -size_t BufferedInStream::overrun(size_t itemSize, size_t nItems, bool wait) +bool BufferedInStream::overrun(size_t needed, bool wait) { - if (itemSize > bufSize) + if (needed > bufSize) throw Exception("BufferedInStream overrun: " "requested size of %lu bytes exceeds maximum of %lu bytes", - (long unsigned)itemSize, (long unsigned)bufSize); + (long unsigned)needed, (long unsigned)bufSize); if (end - ptr != 0) memmove(start, ptr, end - ptr); @@ -58,15 +58,10 @@ size_t BufferedInStream::overrun(size_t itemSize, size_t nItems, bool wait) end -= ptr - start; ptr = start; - while (avail() < itemSize) { + while (avail() < needed) { if (!fillBuffer(start + bufSize - end, wait)) - return 0; + return false; } - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } diff --git a/common/rdr/BufferedInStream.h b/common/rdr/BufferedInStream.h index fc62133..61e57d6 100644 --- a/common/rdr/BufferedInStream.h +++ b/common/rdr/BufferedInStream.h @@ -38,7 +38,7 @@ namespace rdr { private: virtual bool fillBuffer(size_t maxSize, bool wait) = 0; - virtual size_t overrun(size_t itemSize, size_t nItems, bool wait); + virtual bool overrun(size_t needed, bool wait); private: size_t bufSize; diff --git a/common/rdr/BufferedOutStream.cxx b/common/rdr/BufferedOutStream.cxx index 76b0163..69e877a 100644 --- a/common/rdr/BufferedOutStream.cxx +++ b/common/rdr/BufferedOutStream.cxx @@ -71,22 +71,22 @@ void BufferedOutStream::flush() ptr = sentUpTo = start; } -size_t BufferedOutStream::overrun(size_t itemSize, size_t nItems) +void BufferedOutStream::overrun(size_t needed) { - if (itemSize > bufSize) + if (needed > bufSize) throw Exception("BufferedOutStream overrun: " "requested size of %lu bytes exceeds maximum of %lu bytes", - (long unsigned)itemSize, (long unsigned)bufSize); + (long unsigned)needed, (long unsigned)bufSize); // First try to get rid of the data we have flush(); // Still not enough space? - while (itemSize > avail()) { + while (needed > avail()) { // Can we shuffle things around? // (don't do this if it gains us less than 25%) if (((size_t)(sentUpTo - start) > bufSize / 4) && - (itemSize < bufSize - (ptr - sentUpTo))) { + (needed < bufSize - (ptr - sentUpTo))) { memmove(start, sentUpTo, ptr - sentUpTo); ptr = start + (ptr - sentUpTo); sentUpTo = start; @@ -105,11 +105,4 @@ size_t BufferedOutStream::overrun(size_t itemSize, size_t nItems) ptr = sentUpTo = start; } } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; } diff --git a/common/rdr/BufferedOutStream.h b/common/rdr/BufferedOutStream.h index 092ea1f..c33621b 100644 --- a/common/rdr/BufferedOutStream.h +++ b/common/rdr/BufferedOutStream.h @@ -46,7 +46,7 @@ namespace rdr { virtual bool flushBuffer(bool wait) = 0; - virtual size_t overrun(size_t itemSize, size_t nItems); + virtual void overrun(size_t needed); private: size_t bufSize; diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx index 0901afe..ab98298 100644 --- a/common/rdr/HexInStream.cxx +++ b/common/rdr/HexInStream.cxx @@ -73,7 +73,7 @@ decodeError: bool HexInStream::fillBuffer(size_t maxSize, bool wait) { - if (!in_stream.check(2, 1, wait)) + if (!in_stream.check(2, wait)) return false; const U8* iptr = in_stream.getptr(); diff --git a/common/rdr/HexOutStream.cxx b/common/rdr/HexOutStream.cxx index 6118c13..a44c399 100644 --- a/common/rdr/HexOutStream.cxx +++ b/common/rdr/HexOutStream.cxx @@ -95,18 +95,10 @@ HexOutStream::flush() { out_stream.flush(); } -size_t -HexOutStream::overrun(size_t itemSize, size_t nItems) { - if (itemSize > bufSize) - throw Exception("HexOutStream overrun: max itemSize exceeded"); +void HexOutStream::overrun(size_t needed) { + if (needed > bufSize) + throw Exception("HexOutStream overrun: buffer size exceeded"); writeBuffer(); - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; } diff --git a/common/rdr/HexOutStream.h b/common/rdr/HexOutStream.h index 92442a7..2982768 100644 --- a/common/rdr/HexOutStream.h +++ b/common/rdr/HexOutStream.h @@ -37,7 +37,7 @@ namespace rdr { private: void writeBuffer(); - size_t overrun(size_t itemSize, size_t nItems); + virtual void overrun(size_t needed); OutStream& out_stream; diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h index f6b6df5..fc99321 100644 --- a/common/rdr/InStream.h +++ b/common/rdr/InStream.h @@ -43,28 +43,17 @@ namespace rdr { return end - ptr; } - // check() ensures there is buffer data for at least one item of size - // itemSize bytes. Returns the number of items in the buffer (up to a - // maximum of nItems). If wait is false, then instead of blocking to wait - // for the bytes, zero is returned if the bytes are not immediately - // available. If itemSize or nItems is zero, check() will return zero. + // check() ensures there is buffer data for at least needed bytes. Returns + // true once the data is available. If wait is false, then instead of + // blocking to wait for the bytes, false is returned if the bytes are not + // immediately available. - inline size_t check(size_t itemSize, size_t nItems=1, bool wait=true) + inline size_t check(size_t needed, bool wait=true) { - size_t nAvail; + if (needed > avail()) + return overrun(needed, wait); - if (itemSize == 0 || nItems == 0) - return 0; - - if (itemSize > avail()) - return overrun(itemSize, nItems, wait); - - // itemSize cannot be zero at this point - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + return true; } // checkNoWait() tries to make sure that the given number of bytes can @@ -72,10 +61,7 @@ namespace rdr { // otherwise. The length must be "small" (less than the buffer size). // If length is zero, checkNoWait() will return true. - inline bool checkNoWait(size_t length) - { - return length == 0 || check(length, 1, false) > 0; - } + inline bool checkNoWait(size_t length) { return check(length, false); } // readU/SN() methods read unsigned and signed N-bit integers. @@ -146,13 +132,12 @@ namespace rdr { private: // overrun() is implemented by a derived class to cope with buffer overrun. - // It ensures there are at least itemSize bytes of buffer data. Returns - // the number of items in the buffer (up to a maximum of nItems). itemSize - // is supposed to be "small" (a few bytes). If wait is false, then - // instead of blocking to wait for the bytes, zero is returned if the bytes - // are not immediately available. + // It ensures there are at least needed bytes of buffer data. Returns true + // once the data is available. If wait is false, then instead of blocking + // to wait for the bytes, false is returned if the bytes are not + // immediately available. - virtual size_t overrun(size_t itemSize, size_t nItems, bool wait=true) = 0; + virtual bool overrun(size_t needed, bool wait=true) = 0; protected: diff --git a/common/rdr/MemInStream.h b/common/rdr/MemInStream.h index 3e9e77b..83740dd 100644 --- a/common/rdr/MemInStream.h +++ b/common/rdr/MemInStream.h @@ -53,7 +53,7 @@ namespace rdr { private: - size_t overrun(size_t itemSize, size_t nItems, bool wait) { throw EndOfStream(); } + bool overrun(size_t needed, bool wait) { throw EndOfStream(); } const U8* start; bool deleteWhenDone; }; diff --git a/common/rdr/MemOutStream.h b/common/rdr/MemOutStream.h index a839473..f8b4d93 100644 --- a/common/rdr/MemOutStream.h +++ b/common/rdr/MemOutStream.h @@ -52,11 +52,11 @@ namespace rdr { protected: - // overrun() either doubles the buffer or adds enough space for nItems of - // size itemSize bytes. + // overrun() either doubles the buffer or adds enough space for + // needed bytes. - size_t overrun(size_t itemSize, size_t nItems) { - size_t len = ptr - start + itemSize * nItems; + virtual void overrun(size_t needed) { + size_t len = ptr - start + needed; if (len < (size_t)(end - start) * 2) len = (end - start) * 2; @@ -69,8 +69,6 @@ namespace rdr { delete [] start; start = newStart; end = newStart + len; - - return nItems; } U8* start; diff --git a/common/rdr/OutStream.h b/common/rdr/OutStream.h index d7b3a6a..bb88f32 100644 --- a/common/rdr/OutStream.h +++ b/common/rdr/OutStream.h @@ -48,22 +48,12 @@ namespace rdr { return end - ptr; } - // check() ensures there is buffer space for at least one item of size - // itemSize bytes. Returns the number of items which fit (up to a maximum - // of nItems). + // check() ensures there is buffer space for at least needed bytes. - inline size_t check(size_t itemSize, size_t nItems=1) + inline void check(size_t needed) { - size_t nAvail; - - if (itemSize > avail()) - return overrun(itemSize, nItems); - - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; + if (needed > avail()) + overrun(needed); } // writeU/SN() methods write unsigned and signed N-bit integers. @@ -91,19 +81,14 @@ namespace rdr { while (bytes-- > 0) writeU8(0); } - inline void skip(size_t bytes) { - while (bytes > 0) { - size_t n = check(1, bytes); - ptr += n; - bytes -= n; - } - } - // writeBytes() writes an exact number of bytes. void writeBytes(const void* data, size_t length) { while (length > 0) { - size_t n = check(1, length); + check(1); + size_t n = length; + if (length > avail()) + n = avail(); memcpy(ptr, data, n); ptr += n; data = (U8*)data + n; @@ -115,7 +100,10 @@ namespace rdr { void copyBytes(InStream* is, size_t length) { while (length > 0) { - size_t n = check(1, length); + check(1); + size_t n = length; + if (length > avail()) + n = avail(); is->readBytes(ptr, n); ptr += n; length -= n; @@ -151,11 +139,9 @@ namespace rdr { private: // overrun() is implemented by a derived class to cope with buffer overrun. - // It ensures there are at least itemSize bytes of buffer space. Returns - // the number of items which fit (up to a maximum of nItems). itemSize is - // supposed to be "small" (a few bytes). + // It ensures there are at least needed bytes of buffer space. - virtual size_t overrun(size_t itemSize, size_t nItems) = 0; + virtual void overrun(size_t needed) = 0; protected: diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx index 92c2f0c..70303db 100644 --- a/common/rdr/TLSInStream.cxx +++ b/common/rdr/TLSInStream.cxx @@ -36,7 +36,7 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) InStream *in = self->in; try { - if (!in->check(1, 1, false)) { + if (!in->check(1, false)) { gnutls_transport_set_errno(self->session, EAGAIN); return -1; } @@ -84,7 +84,7 @@ size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait) int n; if (gnutls_record_check_pending(session) == 0) { - n = in->check(1, 1, wait); + n = in->check(1, wait); if (n == 0) return 0; } diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx index 089aa66..76f5dff 100644 --- a/common/rdr/TLSOutStream.cxx +++ b/common/rdr/TLSOutStream.cxx @@ -93,19 +93,12 @@ void TLSOutStream::flush() out->flush(); } -size_t TLSOutStream::overrun(size_t itemSize, size_t nItems) +void TLSOutStream::overrun(size_t needed) { - if (itemSize > bufSize) - throw Exception("TLSOutStream overrun: max itemSize exceeded"); + if (needed > bufSize) + throw Exception("TLSOutStream overrun: buffer size exceeded"); flush(); - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; } size_t TLSOutStream::writeTLS(const U8* data, size_t length) diff --git a/common/rdr/TLSOutStream.h b/common/rdr/TLSOutStream.h index 71a7f3b..9ce6eb6 100644 --- a/common/rdr/TLSOutStream.h +++ b/common/rdr/TLSOutStream.h @@ -39,7 +39,7 @@ namespace rdr { size_t length(); protected: - size_t overrun(size_t itemSize, size_t nItems); + virtual void overrun(size_t needed); private: size_t writeTLS(const U8* data, size_t length); diff --git a/common/rdr/ZlibInStream.cxx b/common/rdr/ZlibInStream.cxx index 675600d..2e4e080 100644 --- a/common/rdr/ZlibInStream.cxx +++ b/common/rdr/ZlibInStream.cxx @@ -94,7 +94,7 @@ bool ZlibInStream::fillBuffer(size_t maxSize, bool wait) zs->next_out = (U8*)end; zs->avail_out = maxSize; - size_t n = underlying->check(1, 1, wait); + size_t n = underlying->check(1, wait); if (n == 0) return false; zs->next_in = (U8*)underlying->getptr(); zs->avail_in = underlying->avail(); diff --git a/common/rdr/ZlibOutStream.cxx b/common/rdr/ZlibOutStream.cxx index fac08bd..4a1af19 100644 --- a/common/rdr/ZlibOutStream.cxx +++ b/common/rdr/ZlibOutStream.cxx @@ -95,18 +95,18 @@ void ZlibOutStream::flush() ptr = start; } -size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems) +void ZlibOutStream::overrun(size_t needed) { #ifdef ZLIBOUT_DEBUG fprintf(stderr,"zos overrun\n"); #endif - if (itemSize > bufSize) - throw Exception("ZlibOutStream overrun: max itemSize exceeded"); + if (needed > bufSize) + throw Exception("ZlibOutStream overrun: buffer size exceeded"); checkCompressionLevel(); - while (avail() < itemSize) { + while (avail() < needed) { zs->next_in = start; zs->avail_in = ptr - start; @@ -126,13 +126,6 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems) ptr -= zs->next_in - start; } } - - size_t nAvail; - nAvail = avail() / itemSize; - if (nAvail < nItems) - return nAvail; - - return nItems; } void ZlibOutStream::deflate(int flush) diff --git a/common/rdr/ZlibOutStream.h b/common/rdr/ZlibOutStream.h index 11bb046..aa1875c 100644 --- a/common/rdr/ZlibOutStream.h +++ b/common/rdr/ZlibOutStream.h @@ -45,7 +45,7 @@ namespace rdr { private: - size_t overrun(size_t itemSize, size_t nItems); + virtual void overrun(size_t needed); void deflate(int flush); void checkCompressionLevel(); diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx index 44b73da..ddef733 100644 --- a/common/rfb/CMsgWriter.cxx +++ b/common/rfb/CMsgWriter.cxx @@ -57,7 +57,7 @@ void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf) void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings) { startMsg(msgTypeSetEncodings); - os->skip(1); + os->pad(1); os->writeU16(nEncodings); for (int i = 0; i < nEncodings; i++) os->writeU32(encodings[i]); diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx index 9a5f8d2..fee3d93 100644 --- a/common/rfb/JpegCompressor.cxx +++ b/common/rfb/JpegCompressor.cxx @@ -95,7 +95,7 @@ JpegEmptyOutputBuffer(j_compress_ptr cinfo) JpegCompressor *jc = dest->instance; jc->setptr(jc->getend()); - jc->overrun(jc->getend() - jc->getstart(), 1); + jc->overrun(jc->getend() - jc->getstart()); dest->pub.next_output_byte = jc->getptr(); dest->pub.free_in_buffer = jc->avail(); diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h index 9c6332b..ab0f9f8 100644 --- a/common/rfb/JpegCompressor.h +++ b/common/rfb/JpegCompressor.h @@ -49,8 +49,8 @@ namespace rfb { inline rdr::U8* getstart() { return start; } - virtual inline size_t overrun(size_t itemSize, size_t nItems) { - return MemOutStream::overrun(itemSize, nItems); + inline virtual void overrun(int needed) { + return MemOutStream::overrun(needed); } private: From 281d65292a2da4d75f8357009eef063556d166c3 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 19 May 2020 21:24:58 +0200 Subject: [PATCH 008/207] Remove special functions from JPEG compressor We can do what we want with the standard methods. --- common/rfb/JpegCompressor.cxx | 2 +- common/rfb/JpegCompressor.h | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx index fee3d93..73fb078 100644 --- a/common/rfb/JpegCompressor.cxx +++ b/common/rfb/JpegCompressor.cxx @@ -95,7 +95,7 @@ JpegEmptyOutputBuffer(j_compress_ptr cinfo) JpegCompressor *jc = dest->instance; jc->setptr(jc->getend()); - jc->overrun(jc->getend() - jc->getstart()); + jc->check(jc->length()); dest->pub.next_output_byte = jc->getptr(); dest->pub.free_in_buffer = jc->avail(); diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h index ab0f9f8..de20173 100644 --- a/common/rfb/JpegCompressor.h +++ b/common/rfb/JpegCompressor.h @@ -47,12 +47,6 @@ namespace rfb { void writeBytes(const void*, int); - inline rdr::U8* getstart() { return start; } - - inline virtual void overrun(int needed) { - return MemOutStream::overrun(needed); - } - private: struct jpeg_compress_struct *cinfo; From 25995e2490c19aae600872caadc5fef33055ad11 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 19 May 2020 19:49:41 +0200 Subject: [PATCH 009/207] Remove unused bufSize argument from streams --- common/rdr/BufferedInStream.cxx | 4 ++-- common/rdr/BufferedInStream.h | 2 +- common/rdr/BufferedOutStream.cxx | 4 ++-- common/rdr/BufferedOutStream.h | 2 +- common/rdr/FdInStream.cxx | 11 ++++------- common/rdr/FdInStream.h | 6 ++---- common/rdr/FdOutStream.cxx | 5 ++--- common/rdr/FdOutStream.h | 2 +- common/rdr/HexInStream.cxx | 4 ++-- common/rdr/HexInStream.h | 2 +- common/rdr/HexOutStream.cxx | 4 ++-- common/rdr/HexOutStream.h | 2 +- common/rdr/ZlibInStream.cxx | 5 ++--- common/rdr/ZlibInStream.h | 2 +- common/rdr/ZlibOutStream.cxx | 4 ++-- common/rdr/ZlibOutStream.h | 2 +- common/rfb/ZRLEEncoder.cxx | 2 +- 17 files changed, 28 insertions(+), 35 deletions(-) diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx index 5db3953..64cdb66 100644 --- a/common/rdr/BufferedInStream.cxx +++ b/common/rdr/BufferedInStream.cxx @@ -28,8 +28,8 @@ using namespace rdr; static const size_t DEFAULT_BUF_SIZE = 8192; -BufferedInStream::BufferedInStream(size_t bufSize_) - : bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +BufferedInStream::BufferedInStream() + : bufSize(DEFAULT_BUF_SIZE), offset(0) { ptr = end = start = new U8[bufSize]; } diff --git a/common/rdr/BufferedInStream.h b/common/rdr/BufferedInStream.h index 61e57d6..acf2b92 100644 --- a/common/rdr/BufferedInStream.h +++ b/common/rdr/BufferedInStream.h @@ -46,7 +46,7 @@ namespace rdr { U8* start; protected: - BufferedInStream(size_t bufSize=0); + BufferedInStream(); }; } // end of namespace rdr diff --git a/common/rdr/BufferedOutStream.cxx b/common/rdr/BufferedOutStream.cxx index 69e877a..ac76f6a 100644 --- a/common/rdr/BufferedOutStream.cxx +++ b/common/rdr/BufferedOutStream.cxx @@ -30,8 +30,8 @@ using namespace rdr; static const size_t DEFAULT_BUF_SIZE = 16384; -BufferedOutStream::BufferedOutStream(size_t bufSize_) - : bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +BufferedOutStream::BufferedOutStream() + : bufSize(DEFAULT_BUF_SIZE), offset(0) { ptr = start = sentUpTo = new U8[bufSize]; end = start + bufSize; diff --git a/common/rdr/BufferedOutStream.h b/common/rdr/BufferedOutStream.h index c33621b..8e3229d 100644 --- a/common/rdr/BufferedOutStream.h +++ b/common/rdr/BufferedOutStream.h @@ -57,7 +57,7 @@ namespace rdr { U8* sentUpTo; protected: - BufferedOutStream(size_t bufSize=0); + BufferedOutStream(); }; } diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index c9d2241..d275338 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -48,19 +48,16 @@ using namespace rdr; enum { DEFAULT_BUF_SIZE = 8192 }; -FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_, +FdInStream::FdInStream(int fd_, int timeoutms_, bool closeWhenDone_) - : BufferedInStream(bufSize_), - fd(fd_), closeWhenDone(closeWhenDone_), + : fd(fd_), closeWhenDone(closeWhenDone_), timeoutms(timeoutms_), blockCallback(0), timing(false), timeWaitedIn100us(5), timedKbits(0) { } -FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_, - size_t bufSize_) - : BufferedInStream(bufSize_), - fd(fd_), timeoutms(0), blockCallback(blockCallback_), +FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_) + : fd(fd_), timeoutms(0), blockCallback(blockCallback_), timing(false), timeWaitedIn100us(5), timedKbits(0) { } diff --git a/common/rdr/FdInStream.h b/common/rdr/FdInStream.h index f7a52ba..82280f9 100644 --- a/common/rdr/FdInStream.h +++ b/common/rdr/FdInStream.h @@ -37,10 +37,8 @@ namespace rdr { public: - FdInStream(int fd, int timeoutms=-1, size_t bufSize=0, - bool closeWhenDone_=false); - FdInStream(int fd, FdInStreamBlockCallback* blockCallback, - size_t bufSize=0); + FdInStream(int fd, int timeoutms=-1, bool closeWhenDone_=false); + FdInStream(int fd, FdInStreamBlockCallback* blockCallback); virtual ~FdInStream(); void setTimeout(int timeoutms); diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index 4fc7467..d7da710 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -49,9 +49,8 @@ using namespace rdr; -FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, size_t bufSize_) - : BufferedOutStream(bufSize_), - fd(fd_), blocking(blocking_), timeoutms(timeoutms_) +FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_) + : fd(fd_), blocking(blocking_), timeoutms(timeoutms_) { gettimeofday(&lastWrite, NULL); } diff --git a/common/rdr/FdOutStream.h b/common/rdr/FdOutStream.h index b1ecbd5..2432797 100644 --- a/common/rdr/FdOutStream.h +++ b/common/rdr/FdOutStream.h @@ -34,7 +34,7 @@ namespace rdr { public: - FdOutStream(int fd, bool blocking=true, int timeoutms=-1, size_t bufSize=0); + FdOutStream(int fd, bool blocking=true, int timeoutms=-1); virtual ~FdOutStream(); void setTimeout(int timeoutms); diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx index ab98298..3800961 100644 --- a/common/rdr/HexInStream.cxx +++ b/common/rdr/HexInStream.cxx @@ -26,8 +26,8 @@ using namespace rdr; static inline int min(int a, int b) {return azalloc = Z_NULL; diff --git a/common/rdr/ZlibOutStream.h b/common/rdr/ZlibOutStream.h index aa1875c..2b08f8d 100644 --- a/common/rdr/ZlibOutStream.h +++ b/common/rdr/ZlibOutStream.h @@ -35,7 +35,7 @@ namespace rdr { public: - ZlibOutStream(OutStream* os=0, size_t bufSize=0, int compressionLevel=-1); + ZlibOutStream(OutStream* os=0, int compressionLevel=-1); virtual ~ZlibOutStream(); void setUnderlying(OutStream* os); diff --git a/common/rfb/ZRLEEncoder.cxx b/common/rfb/ZRLEEncoder.cxx index 8917d8f..f1a3262 100644 --- a/common/rfb/ZRLEEncoder.cxx +++ b/common/rfb/ZRLEEncoder.cxx @@ -31,7 +31,7 @@ IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1); ZRLEEncoder::ZRLEEncoder(SConnection* conn) : Encoder(conn, encodingZRLE, EncoderPlain, 127), - zos(0,0,zlibLevel), mos(129*1024) + zos(0,zlibLevel), mos(129*1024) { zos.setUnderlying(&mos); } From a55f142c9835502b5d6afe31ae0b9d8acbfb55ac Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 16 May 2020 11:32:26 +0200 Subject: [PATCH 010/207] Don't shuffle input buffer unless actually needed --- common/rdr/BufferedInStream.cxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx index 64cdb66..967e5a5 100644 --- a/common/rdr/BufferedInStream.cxx +++ b/common/rdr/BufferedInStream.cxx @@ -51,12 +51,14 @@ bool BufferedInStream::overrun(size_t needed, bool wait) "requested size of %lu bytes exceeds maximum of %lu bytes", (long unsigned)needed, (long unsigned)bufSize); - if (end - ptr != 0) + // Do we need to shuffle things around? + if ((bufSize - (ptr - start)) < needed) { memmove(start, ptr, end - ptr); - offset += ptr - start; - end -= ptr - start; - ptr = start; + offset += ptr - start; + end -= ptr - start; + ptr = start; + } while (avail() < needed) { if (!fillBuffer(start + bufSize - end, wait)) From fde088ce65b977bc504e09f56119ffa20dba7596 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 21 May 2020 11:31:40 +0200 Subject: [PATCH 011/207] Improved bandwidth monitoring Now measures over an entire update, which should hopefully give us more stable values. They are still small values for fast networks though so increase precision in the values we keep. --- common/rdr/FdInStream.cxx | 53 ++------------------------------------- common/rdr/FdInStream.h | 9 ------- 2 files changed, 2 insertions(+), 60 deletions(-) diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index d275338..27de92b 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -51,14 +51,12 @@ enum { DEFAULT_BUF_SIZE = 8192 }; FdInStream::FdInStream(int fd_, int timeoutms_, bool closeWhenDone_) : fd(fd_), closeWhenDone(closeWhenDone_), - timeoutms(timeoutms_), blockCallback(0), - timing(false), timeWaitedIn100us(5), timedKbits(0) + timeoutms(timeoutms_), blockCallback(0) { } FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_) - : fd(fd_), timeoutms(0), blockCallback(blockCallback_), - timing(false), timeWaitedIn100us(5), timedKbits(0) + : fd(fd_), timeoutms(0), blockCallback(blockCallback_) { } @@ -104,10 +102,6 @@ bool FdInStream::fillBuffer(size_t maxSize, bool wait) size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait) { - struct timeval before, after; - if (timing) - gettimeofday(&before, 0); - int n; while (true) { do { @@ -144,48 +138,5 @@ size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait) if (n < 0) throw SystemException("read",errno); if (n == 0) throw EndOfStream(); - if (timing) { - gettimeofday(&after, 0); - int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 + - (after.tv_usec - before.tv_usec) / 100); - int newKbits = n * 8 / 1000; - - // limit rate to between 10kbit/s and 40Mbit/s - - if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000; - if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4; - - timeWaitedIn100us += newTimeWaited; - timedKbits += newKbits; - } - return n; } - -void FdInStream::startTiming() -{ - timing = true; - - // Carry over up to 1s worth of previous rate for smoothing. - - if (timeWaitedIn100us > 10000) { - timedKbits = timedKbits * 10000 / timeWaitedIn100us; - timeWaitedIn100us = 10000; - } -} - -void FdInStream::stopTiming() -{ - timing = false; - if (timeWaitedIn100us < timedKbits/2) - timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s -} - -unsigned int FdInStream::kbitsPerSecond() -{ - // The following calculation will overflow 32-bit arithmetic if we have - // received more than about 50Mbytes (400Mbits) since we started timing, so - // it should be OK for a single RFB update. - - return timedKbits * 10000 / timeWaitedIn100us; -} diff --git a/common/rdr/FdInStream.h b/common/rdr/FdInStream.h index 82280f9..0203389 100644 --- a/common/rdr/FdInStream.h +++ b/common/rdr/FdInStream.h @@ -45,11 +45,6 @@ namespace rdr { void setBlockCallback(FdInStreamBlockCallback* blockCallback); int getFd() { return fd; } - void startTiming(); - void stopTiming(); - unsigned int kbitsPerSecond(); - unsigned int timeWaited() { return timeWaitedIn100us; } - private: virtual bool fillBuffer(size_t maxSize, bool wait); @@ -60,10 +55,6 @@ namespace rdr { int timeoutms; FdInStreamBlockCallback* blockCallback; - bool timing; - unsigned int timeWaitedIn100us; - unsigned int timedKbits; - size_t offset; U8* start; }; From f54dc7829a2dfa9009d9219931f8778235cfcab0 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 16 Nov 2020 13:37:44 +0100 Subject: [PATCH 012/207] Update keycode maps to latest version Mainly fixes for Japanese and Korean keyboard layouts. --- unix/xserver/hw/vnc/qnum_to_xorgevdev.c | 14 +++++++------- unix/xserver/hw/vnc/qnum_to_xorgkbd.c | 10 +++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/unix/xserver/hw/vnc/qnum_to_xorgevdev.c b/unix/xserver/hw/vnc/qnum_to_xorgevdev.c index 357c88d..c93d5d2 100644 --- a/unix/xserver/hw/vnc/qnum_to_xorgevdev.c +++ b/unix/xserver/hw/vnc/qnum_to_xorgevdev.c @@ -1,8 +1,8 @@ /* - * This file is auto-generated from keymaps.csv on 2017-08-28 13:03 - * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097) + * This file is auto-generated from keymaps.csv + * Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920) * To re-generate, run: - * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgevdev + * keymap-gen code-map --lang=stdc keymaps.csv qnum xorgevdev */ const unsigned short code_map_qnum_to_xorgevdev[254] = { [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgevdev:9 */ @@ -112,9 +112,9 @@ const unsigned short code_map_qnum_to_xorgevdev[254] = { [0x6c] = 0xa9, /* qnum:108 -> linux:161 (KEY_EJECTCD) -> xorgevdev:169 */ [0x6d] = 0xc9, /* qnum:109 -> linux:193 (KEY_F23) -> xorgevdev:201 */ [0x6f] = 0xca, /* qnum:111 -> linux:194 (KEY_F24) -> xorgevdev:202 */ - [0x70] = 0xb2, /* qnum:112 -> linux:170 (KEY_ISO) -> xorgevdev:178 */ - [0x71] = 0xb6, /* qnum:113 -> linux:174 (KEY_EXIT) -> xorgevdev:182 */ - [0x72] = 0xb7, /* qnum:114 -> linux:175 (KEY_MOVE) -> xorgevdev:183 */ + [0x70] = 0x65, /* qnum:112 -> linux:93 (KEY_KATAKANAHIRAGANA) -> xorgevdev:101 */ + [0x71] = 0x83, /* qnum:113 -> linux:123 (KEY_HANJA) -> xorgevdev:131 */ + [0x72] = 0x82, /* qnum:114 -> linux:122 (KEY_HANGEUL) -> xorgevdev:130 */ [0x73] = 0x61, /* qnum:115 -> linux:89 (KEY_RO) -> xorgevdev:97 */ [0x74] = 0xc7, /* qnum:116 -> linux:191 (KEY_F21) -> xorgevdev:199 */ [0x75] = 0xb9, /* qnum:117 -> linux:177 (KEY_SCROLLUP) -> xorgevdev:185 */ @@ -137,7 +137,6 @@ const unsigned short code_map_qnum_to_xorgevdev[254] = { [0x8a] = 0xbe, /* qnum:138 -> linux:182 (KEY_REDO) -> xorgevdev:190 */ [0x8b] = 0x80, /* qnum:139 -> linux:120 (KEY_SCALE) -> xorgevdev:128 */ [0x8c] = 0x8c, /* qnum:140 -> linux:132 (KEY_FRONT) -> xorgevdev:140 */ - [0x8d] = 0x83, /* qnum:141 -> linux:123 (KEY_HANJA) -> xorgevdev:131 */ [0x8e] = 0xf1, /* qnum:142 -> linux:233 (KEY_FORWARDMAIL) -> xorgevdev:241 */ [0x8f] = 0xba, /* qnum:143 -> linux:178 (KEY_SCROLLDOWN) -> xorgevdev:186 */ [0x90] = 0xad, /* qnum:144 -> linux:165 (KEY_PREVIOUSSONG) -> xorgevdev:173 */ @@ -177,6 +176,7 @@ const unsigned short code_map_qnum_to_xorgevdev[254] = { [0xb4] = 0xd8, /* qnum:180 -> linux:208 (KEY_FASTFORWARD) -> xorgevdev:216 */ [0xb5] = 0x6a, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgevdev:106 */ [0xb6] = 0xd9, /* qnum:182 -> linux:209 (KEY_BASSBOOST) -> xorgevdev:217 */ + [0xb7] = 0x6b, /* qnum:183 -> linux:99 (KEY_SYSRQ) -> xorgevdev:107 */ [0xb8] = 0x6c, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgevdev:108 */ [0xb9] = 0xda, /* qnum:185 -> linux:210 (KEY_PRINT) -> xorgevdev:218 */ [0xba] = 0xdb, /* qnum:186 -> linux:211 (KEY_HP) -> xorgevdev:219 */ diff --git a/unix/xserver/hw/vnc/qnum_to_xorgkbd.c b/unix/xserver/hw/vnc/qnum_to_xorgkbd.c index 57c2047..86632db 100644 --- a/unix/xserver/hw/vnc/qnum_to_xorgkbd.c +++ b/unix/xserver/hw/vnc/qnum_to_xorgkbd.c @@ -1,8 +1,8 @@ /* - * This file is auto-generated from keymaps.csv on 2017-08-28 13:04 - * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097) + * This file is auto-generated from keymaps.csv + * Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920) * To re-generate, run: - * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgkbd + * keymap-gen code-map --lang=stdc keymaps.csv qnum xorgkbd */ const unsigned short code_map_qnum_to_xorgkbd[254] = { [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgkbd:9 */ @@ -97,11 +97,15 @@ const unsigned short code_map_qnum_to_xorgkbd[254] = { [0x5d] = 0x76, /* qnum:93 -> linux:183 (KEY_F13) -> xorgkbd:118 */ [0x5e] = 0x77, /* qnum:94 -> linux:184 (KEY_F14) -> xorgkbd:119 */ [0x5f] = 0x78, /* qnum:95 -> linux:185 (KEY_F15) -> xorgkbd:120 */ + [0x70] = 0xd0, /* qnum:112 -> linux:93 (KEY_KATAKANAHIRAGANA) -> xorgkbd:208 */ + [0x71] = 0x7a, /* qnum:113 -> linux:123 (KEY_HANJA) -> xorgkbd:122 */ + [0x72] = 0x79, /* qnum:114 -> linux:122 (KEY_HANGEUL) -> xorgkbd:121 */ [0x7d] = 0x85, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgkbd:133 */ [0x83] = 0x7a, /* qnum:131 -> linux:187 (KEY_F17) -> xorgkbd:122 */ [0x9c] = 0x6c, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgkbd:108 */ [0x9d] = 0x6d, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgkbd:109 */ [0xb5] = 0x70, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgkbd:112 */ + [0xb7] = 0x6f, /* qnum:183 -> linux:99 (KEY_SYSRQ) -> xorgkbd:111 */ [0xb8] = 0x71, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgkbd:113 */ [0xc6] = 0x6e, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgkbd:110 */ [0xc7] = 0x61, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgkbd:97 */ From 189f503b987416a28ff58504dd416979bde46ed7 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 14 Jan 2021 13:07:56 +0100 Subject: [PATCH 013/207] Fix handling of bad update requests We computed a safe area if a client gave us a bogus one, but we didn't actually use it. Fix this properly and make sure we don't pass on bad coordinates further. --- common/rfb/VNCSConnectionST.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 9e7f3de..114c922 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -874,7 +874,7 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) // Just update the requested region. // Framebuffer update will be sent a bit later, see processMessages(). - Region reqRgn(r); + Region reqRgn(safeRect); if (!incremental || !continuousUpdates) requested.assign_union(reqRgn); From da83ecf86aa565881ca5055fc60f83f7c41d2d58 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 1 Mar 2021 15:46:35 +0100 Subject: [PATCH 014/207] Drop other selection on ownership change Otherwise we might end up owners of something we cannot deliver data on, which can hang applications. --- unix/xserver/hw/vnc/vncSelection.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 51dfd9c..57e13e7 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -512,6 +512,13 @@ static void vncSelectionCallback(CallbackListPtr *callbacks, LOG_DEBUG("Selection owner change for %s", NameForAtom(info->selection->selection)); + /* + * If we're the previous owner of this selection, then we're also the + * owner of _the other_ selection. Make sure we drop all ownerships so + * we either own both selections or nonw. + */ + DeleteWindowFromAnySelections(pWindow); + if ((info->selection->selection != xaPRIMARY) && (info->selection->selection != xaCLIPBOARD)) return; From d8caab699d1e1974c6a0b5a765750f1b7c3200fc Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 2 Mar 2021 13:14:50 +0100 Subject: [PATCH 015/207] Add missing headers for TCP_CORK We didn't include the proper headers to get the correct define, so corking was never enabled. --- common/rdr/FdOutStream.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index d7da710..c4ca03f 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -35,6 +35,8 @@ #include #include #include +#include +#include #endif /* Old systems have select() in sys/time.h */ From 25b8e64adbfc337dd096d9138b6ce1a89229b8d7 Mon Sep 17 00:00:00 2001 From: lhchavez Date: Sun, 7 Feb 2021 16:36:47 -0800 Subject: [PATCH 016/207] Add support for notifying clients about pointer movements This change adds support for the VMware Mouse Position pseudo-encoding[1], which is used to notify VNC clients when X11 clients call `XWarpPointer()`[2]. This function is called by SDL (and other similar libraries) when they detect that the server does not support native relative motion, like some RFB clients. With this, RFB clients can choose to adjust the local cursor position under certain circumstances to match what the server has set. For instance, if pointer lock has been enabled on the client's machine and the cursor is not being drawn locally, the local position of the cursor is irrelevant, so the RFB client can use what the server sends as the canonical absolute position of the cursor. This ultimately enables the possibility of games (especially FPS games) to behave how users expect (if the clients implement the corresponding change). Part of: #619 1: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding 2: https://tronche.com/gui/x/xlib/input/XWarpPointer.html 3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804 --- common/rfb/ConnParams.cxx | 11 +++++++- common/rfb/ConnParams.h | 5 ++++ common/rfb/SMsgWriter.cxx | 39 +++++++++++++++++++++++++++ common/rfb/SMsgWriter.h | 5 ++++ common/rfb/VNCSConnectionST.cxx | 24 +++++++++++++++++ common/rfb/VNCSConnectionST.h | 6 +++++ common/rfb/VNCServer.h | 6 +++-- common/rfb/VNCServerST.cxx | 7 +++-- common/rfb/VNCServerST.h | 2 +- common/rfb/encodings.h | 3 +++ unix/xserver/hw/vnc/XserverDesktop.cc | 11 +++++++- unix/xserver/hw/vnc/XserverDesktop.h | 1 + unix/xserver/hw/vnc/vncExtInit.cc | 5 ++++ unix/xserver/hw/vnc/vncExtInit.h | 1 + unix/xserver/hw/vnc/vncHooks.c | 26 ++++++++++++++++++ win/rfb_win32/SDisplay.cxx | 2 +- 16 files changed, 146 insertions(+), 8 deletions(-) diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx index 7942779..4ef46c6 100644 --- a/common/rfb/ConnParams.cxx +++ b/common/rfb/ConnParams.cxx @@ -36,6 +36,7 @@ ConnParams::ConnParams() width(0), height(0), useCopyRect(false), supportsLocalCursor(false), supportsLocalXCursor(false), supportsLocalCursorWithAlpha(false), + supportsCursorPosition(false), supportsDesktopResize(false), supportsExtendedDesktopSize(false), supportsDesktopRename(false), supportsLastRect(false), supportsLEDState(false), supportsQEMUKeyEvent(false), @@ -43,7 +44,7 @@ ConnParams::ConnParams() supportsSetDesktopSize(false), supportsFence(false), supportsContinuousUpdates(false), compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), - subsampling(subsampleUndefined), name_(0), verStrPos(0), + subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0), ledState_(ledUnknown), shandler(NULL) { memset(kasmPassed, 0, KASM_NUM_SETTINGS); @@ -101,6 +102,11 @@ void ConnParams::setCursor(const Cursor& other) cursor_ = new Cursor(other); } +void ConnParams::setCursorPos(const Point& pos) +{ + cursorPos_ = pos; +} + bool ConnParams::supportsEncoding(rdr::S32 encoding) const { return encodings_.count(encoding) != 0; @@ -147,6 +153,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings) case pseudoEncodingExtendedDesktopSize: supportsExtendedDesktopSize = true; break; + case pseudoEncodingVMwareCursorPosition: + supportsCursorPosition = true; + break; case pseudoEncodingDesktopName: supportsDesktopRename = true; break; diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h index 7439943..844f4bc 100644 --- a/common/rfb/ConnParams.h +++ b/common/rfb/ConnParams.h @@ -84,6 +84,9 @@ namespace rfb { const Cursor& cursor() const { return *cursor_; } void setCursor(const Cursor& cursor); + const Point& cursorPos() const { return cursorPos_; } + void setCursorPos(const Point& pos); + bool supportsEncoding(rdr::S32 encoding) const; void setEncodings(int nEncodings, const rdr::S32* encodings); @@ -96,6 +99,7 @@ namespace rfb { bool supportsLocalCursor; bool supportsLocalXCursor; bool supportsLocalCursorWithAlpha; + bool supportsCursorPosition; bool supportsDesktopResize; bool supportsExtendedDesktopSize; bool supportsDesktopRename; @@ -136,6 +140,7 @@ namespace rfb { PixelFormat pf_; char* name_; Cursor* cursor_; + Point cursorPos_; std::set encodings_; char verStr[13]; int verStrPos; diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index 07622fd..6002d18 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -39,6 +39,7 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_) needSetDesktopSize(false), needExtendedDesktopSize(false), needSetDesktopName(false), needSetCursor(false), needSetXCursor(false), needSetCursorWithAlpha(false), + needCursorPos(false), needLEDState(false), needQEMUKeyEvent(false) { } @@ -204,6 +205,14 @@ bool SMsgWriter::writeSetCursorWithAlpha() return true; } +void SMsgWriter::writeCursorPos() +{ + if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition)) + throw Exception("Client does not support cursor position"); + + needCursorPos = true; +} + bool SMsgWriter::writeLEDState() { if (!cp->supportsLEDState) @@ -232,6 +241,8 @@ bool SMsgWriter::needFakeUpdate() return true; if (needSetCursor || needSetXCursor || needSetCursorWithAlpha) return true; + if (needCursorPos) + return true; if (needLEDState) return true; if (needQEMUKeyEvent) @@ -284,6 +295,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) nRects++; if (needSetCursorWithAlpha) nRects++; + if (needCursorPos) + nRects++; if (needLEDState) nRects++; if (needQEMUKeyEvent) @@ -399,6 +412,18 @@ void SMsgWriter::writePseudoRects() needSetCursorWithAlpha = false; } + if (needCursorPos) { + const Point& cursorPos = cp->cursorPos(); + + if (cp->supportsEncoding(pseudoEncodingVMwareCursorPosition)) { + writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y); + } else { + throw Exception("Client does not support cursor position"); + } + + needCursorPos = false; + } + if (needSetDesktopName) { writeSetDesktopNameRect(cp->name()); needSetDesktopName = false; @@ -577,6 +602,20 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height, } } +void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY) +{ + if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition)) + throw Exception("Client does not support cursor position"); + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync"); + + os->writeS16(hotspotX); + os->writeS16(hotspotY); + os->writeU16(0); + os->writeU16(0); + os->writeU32(pseudoEncodingVMwareCursorPosition); +} + void SMsgWriter::writeLEDStateRect(rdr::U8 state) { if (!cp->supportsLEDState) diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 6945ba4..5d5e9ae 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -83,6 +83,9 @@ namespace rfb { bool writeSetXCursor(); bool writeSetCursorWithAlpha(); + // Notifies the client that the cursor pointer was moved by the server. + void writeCursorPos(); + // Same for LED state message bool writeLEDState(); @@ -138,6 +141,7 @@ namespace rfb { void writeSetCursorWithAlphaRect(int width, int height, int hotspotX, int hotspotY, const rdr::U8* data); + void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY); void writeLEDStateRect(rdr::U8 state); void writeQEMUKeyEventRect(); @@ -153,6 +157,7 @@ namespace rfb { bool needSetCursor; bool needSetXCursor; bool needSetCursorWithAlpha; + bool needCursorPos; bool needLEDState; bool needQEMUKeyEvent; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 114c922..6c96ff5 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -517,6 +517,15 @@ void VNCSConnectionST::renderedCursorChange() } } +// cursorPositionChange() is called whenever the cursor has changed position by +// the server. If the client supports being informed about these changes then +// it will arrange for the new cursor position to be sent to the client. + +void VNCSConnectionST::cursorPositionChange() +{ + setCursorPos(); +} + // needRenderedCursor() returns true if this client needs the server-side // rendered cursor. This may be because it does not support local cursor or // because the current cursor position has not been set by this client. @@ -1482,6 +1491,21 @@ void VNCSConnectionST::setCursor() } } +// setCursorPos() is called whenever the cursor has changed position by the +// server. If the client supports being informed about these changes then it +// will arrange for the new cursor position to be sent to the client. + +void VNCSConnectionST::setCursorPos() +{ + if (state() != RFBSTATE_NORMAL) + return; + + if (cp.supportsCursorPosition) { + cp.setCursorPos(server->cursorPos); + writer()->writeCursorPos(); + } +} + void VNCSConnectionST::setDesktopName(const char *name) { cp.setName(name); diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index d1e1267..f03db37 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -97,6 +97,11 @@ namespace rfb { // cursor. void renderedCursorChange(); + // cursorPositionChange() is called whenever the cursor has changed position by + // the server. If the client supports being informed about these changes then + // it will arrange for the new cursor position to be sent to the client. + void cursorPositionChange(); + // needRenderedCursor() returns true if this client needs the server-side // rendered cursor. This may be because it does not support local cursor // or because the current cursor position has not been set by this client. @@ -223,6 +228,7 @@ namespace rfb { void screenLayoutChange(rdr::U16 reason); void setCursor(); + void setCursorPos(); void setDesktopName(const char *name); void setLEDState(unsigned int state); void setSocketTimeouts(); diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index c5335ad..1f36d8f 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -69,8 +69,10 @@ namespace rfb { virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* cursorData) = 0; - // setCursorPos() tells the server the current position of the cursor. - virtual void setCursorPos(const Point& p) = 0; + // setCursorPos() tells the server the current position of the cursor, and + // whether the server initiated that change (e.g. through another X11 + // client calling XWarpPointer()). + virtual void setCursorPos(const Point& p, bool warped) = 0; // setName() tells the server what desktop title to supply to clients virtual void setName(const char* name) = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 7bec158..f60e091 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -565,14 +565,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot, } } -void VNCServerST::setCursorPos(const Point& pos) +void VNCServerST::setCursorPos(const Point& pos, bool warped) { if (!cursorPos.equals(pos)) { cursorPos = pos; renderedCursorInvalid = true; std::list::iterator ci; - for (ci = clients.begin(); ci != clients.end(); ci++) + for (ci = clients.begin(); ci != clients.end(); ci++) { (*ci)->renderedCursorChange(); + if (warped) + (*ci)->cursorPositionChange(); + } } } diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 7872141..d572813 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -101,7 +101,7 @@ namespace rfb { virtual void add_copied(const Region &dest, const Point &delta); virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* data); - virtual void setCursorPos(const Point& p); + virtual void setCursorPos(const Point& p, bool warped); virtual void setLEDState(unsigned state); virtual void bell(); diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h index 96d82f4..b32b375 100644 --- a/common/rfb/encodings.h +++ b/common/rfb/encodings.h @@ -85,6 +85,9 @@ namespace rfb { const int pseudoEncodingVideoOutTimeLevel1 = -1986; const int pseudoEncodingVideoOutTimeLevel100 = -1887; + // VMware-specific + const int pseudoEncodingVMwareCursorPosition = 0x574d5666; + int encodingNum(const char* name); const char* encodingName(int num); } diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 0f8a241..6a1bb93 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -241,6 +241,15 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY, delete [] cursorData; } +void XserverDesktop::setCursorPos(int x, int y, bool warped) +{ + try { + server->setCursorPos(Point(x, y), warped); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::setCursorPos: %s",e.str()); + } +} + void XserverDesktop::add_changed(const rfb::Region ®ion) { try { @@ -358,7 +367,7 @@ void XserverDesktop::blockHandler(int* timeout) if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) { oldCursorPos.x = cursorX; oldCursorPos.y = cursorY; - server->setCursorPos(oldCursorPos); + server->setCursorPos(oldCursorPos, false); } // Trigger timers and check when the next will expire diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index ec9bf37..d78216e 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -66,6 +66,7 @@ public: void setDesktopName(const char* name); void setCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); + void setCursorPos(int x, int y, bool warped); void add_changed(const rfb::Region ®ion); void add_copied(const rfb::Region &dest, const rfb::Point &delta); void handleSocketEvent(int fd, bool read, bool write); diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 99e00dc..cbc8b6b 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -418,6 +418,11 @@ void vncSetCursor(int width, int height, int hotX, int hotY, desktop[scr]->setCursor(width, height, hotX, hotY, rgbaData); } +void vncSetCursorPos(int scrIdx, int x, int y) +{ + desktop[scrIdx]->setCursorPos(x, y, true); +} + void vncPreScreenResize(int scrIdx) { // We need to prevent the RFB core from accessing the framebuffer diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 9414723..5db6d4b 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -86,6 +86,7 @@ void vncAddCopied(int scrIdx, const struct UpdateRect *extents, void vncSetCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); +void vncSetCursorPos(int scrIdx, int x, int y); void vncPreScreenResize(int scrIdx); void vncPostScreenResize(int scrIdx, int success, int width, int height); diff --git a/unix/xserver/hw/vnc/vncHooks.c b/unix/xserver/hw/vnc/vncHooks.c index e2be124..f48f756 100644 --- a/unix/xserver/hw/vnc/vncHooks.c +++ b/unix/xserver/hw/vnc/vncHooks.c @@ -65,6 +65,9 @@ typedef struct _vncHooksScreenRec { RestoreAreasProcPtr RestoreAreas; #endif DisplayCursorProcPtr DisplayCursor; +#if XORG >= 119 + CursorWarpedToProcPtr CursorWarpedTo; +#endif ScreenBlockHandlerProcPtr BlockHandler; #ifdef RENDER CompositeProcPtr Composite; @@ -137,6 +140,12 @@ static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr prgnExposed); #endif static Bool vncHooksDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr cursor); +#if XORG >= 119 +static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, + ScreenPtr pScreen_, ClientPtr pClient, + WindowPtr pWindow, SpritePtr pSprite, + int x, int y); +#endif #if XORG <= 112 static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout, pointer pReadmask); @@ -316,6 +325,9 @@ int vncHooksInit(int scrIdx) wrap(vncHooksScreen, pScreen, RestoreAreas, vncHooksRestoreAreas); #endif wrap(vncHooksScreen, pScreen, DisplayCursor, vncHooksDisplayCursor); +#if XORG >= 119 + wrap(vncHooksScreen, pScreen, CursorWarpedTo, vncHooksCursorWarpedTo); +#endif wrap(vncHooksScreen, pScreen, BlockHandler, vncHooksBlockHandler); #ifdef RENDER ps = GetPictureScreenIfSet(pScreen); @@ -725,6 +737,20 @@ out: return ret; } +// CursorWarpedTo - notify that the cursor was warped + +#if XORG >= 119 +static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, + ScreenPtr pScreen_, ClientPtr pClient, + WindowPtr pWindow, SpritePtr pSprite, + int x, int y) +{ + SCREEN_PROLOGUE(pScreen_, CursorWarpedTo); + vncSetCursorPos(pScreen->myNum, x, y); + SCREEN_EPILOGUE(CursorWarpedTo); +} +#endif + // BlockHandler - ignore any changes during the block handler - it's likely // these are just drawing the cursor. diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx index 9b2cbb0..1c9a0ac 100644 --- a/win/rfb_win32/SDisplay.cxx +++ b/win/rfb_win32/SDisplay.cxx @@ -385,7 +385,7 @@ SDisplay::processEvent(HANDLE event) { // Update the cursor position // NB: First translate from Screen coordinates to Desktop Point desktopPos = info.position.translate(screenRect.tl.negate()); - server->setCursorPos(desktopPos); + server->setCursorPos(desktopPos, false); old_cursor = info; } From d7198cbe5ddc9523bcc6e67888a59f1a48f62d28 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 14 Feb 2019 16:57:26 +0100 Subject: [PATCH 017/207] Consume data properly in Hextile decoder We accidentally removed the code updating the data index in 8a189a6, resulting in the decoder newer consuming any data. So the data would be parsed as the next rect, causing weird errors. --- kasmweb/core/decoders/hextile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/kasmweb/core/decoders/hextile.js b/kasmweb/core/decoders/hextile.js index 07c86db..aa76d2f 100644 --- a/kasmweb/core/decoders/hextile.js +++ b/kasmweb/core/decoders/hextile.js @@ -129,6 +129,7 @@ export default class HextileDecoder { } display.finishTile(); } + sock.rQi = rQi; this._lastsubencoding = subencoding; this._tiles--; } From 642a67f76f053a6e2fc571ea467e306d7680e195 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 10:22:27 +0100 Subject: [PATCH 018/207] Use arrow function to avoid bind --- kasmweb/core/rfb.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index 91c11b0..2c06cd4 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -203,7 +203,9 @@ export default class RFB extends EventTargetMixin { this._mouse.onmousemove = this._handleMouseMove.bind(this); this._sock = new Websock(); - this._sock.on('message', this._handle_message.bind(this)); + this._sock.on('message', () => { + this._handle_message(); + }); this._sock.on('open', () => { if ((this._rfb_connection_state === 'connecting') && (this._rfb_init_state === '')) { From 41e6fedba69a996ec89dd942f1d13fc66cbee78b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 10:23:32 +0100 Subject: [PATCH 019/207] Fix version handshake to handle slow data --- kasmweb/core/rfb.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index 2c06cd4..2644ace 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -897,8 +897,8 @@ export default class RFB extends EventTargetMixin { // Message Handlers _negotiate_protocol_version() { - if (this._sock.rQlen < 12) { - return this._fail("Received incomplete protocol version."); + if (this._sock.rQwait("version", 12)) { + return false; } const sversion = this._sock.rQshiftStr(12).substr(4, 7); From ce6d66f030f62e629016650de8c578cd05efea4b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 10:24:41 +0100 Subject: [PATCH 020/207] Fix security failure reason handling of slow data Things would break if the security result and security reason did not arrive in the same WebSocket message. --- kasmweb/core/rfb.js | 59 +++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index 2644ace..e6427fd 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -965,7 +965,10 @@ export default class RFB extends EventTargetMixin { if (this._sock.rQwait("security type", num_types, 1)) { return false; } if (num_types === 0) { - return this._handle_security_failure("no security types"); + this._rfb_init_state = "SecurityReason"; + this._security_context = "no security types"; + this._security_status = 1; + return this._init_msg(); } const types = this._sock.rQshiftBytes(num_types); @@ -990,6 +993,13 @@ export default class RFB extends EventTargetMixin { // Server decides if (this._sock.rQwait("security scheme", 4)) { return false; } this._rfb_auth_scheme = this._sock.rQshift32(); + + if (this._rfb_auth_scheme == 0) { + this._rfb_init_state = "SecurityReason"; + this._security_context = "authentication scheme"; + this._security_status = 1; + return this._init_msg(); + } } this._rfb_init_state = 'Authentication'; @@ -998,28 +1008,7 @@ export default class RFB extends EventTargetMixin { return this._init_msg(); // jump to authentication } - /* - * Get the security failure reason if sent from the server and - * send the 'securityfailure' event. - * - * - The optional parameter context can be used to add some extra - * context to the log output. - * - * - The optional parameter security_result_status can be used to - * add a custom status code to the event. - */ - _handle_security_failure(context, security_result_status) { - - if (typeof context === 'undefined') { - context = ""; - } else { - context = " on " + context; - } - - if (typeof security_result_status === 'undefined') { - security_result_status = 1; // fail - } - + _handle_security_reason() { if (this._sock.rQwait("reason length", 4)) { return false; } @@ -1027,23 +1016,26 @@ export default class RFB extends EventTargetMixin { let reason = ""; if (strlen > 0) { - if (this._sock.rQwait("reason", strlen, 8)) { return false; } + if (this._sock.rQwait("reason", strlen, 4)) { return false; } reason = this._sock.rQshiftStr(strlen); } if (reason !== "") { this.dispatchEvent(new CustomEvent( "securityfailure", - { detail: { status: security_result_status, reason: reason } })); + { detail: { status: this._security_status, + reason: reason } })); - return this._fail("Security negotiation failed" + context + + return this._fail("Security negotiation failed on " + + this._security_context + " (reason: " + reason + ")"); } else { this.dispatchEvent(new CustomEvent( "securityfailure", - { detail: { status: security_result_status } })); + { detail: { status: this._security_status } })); - return this._fail("Security negotiation failed" + context); + return this._fail("Security negotiation failed on " + + this._security_context); } } @@ -1185,9 +1177,6 @@ export default class RFB extends EventTargetMixin { _negotiate_authentication() { switch (this._rfb_auth_scheme) { - case 0: // connection failed - return this._handle_security_failure("authentication scheme"); - case 1: // no auth if (this._rfb_version >= 3.8) { this._rfb_init_state = 'SecurityResult'; @@ -1222,7 +1211,10 @@ export default class RFB extends EventTargetMixin { return this._init_msg(); } else { if (this._rfb_version >= 3.8) { - return this._handle_security_failure("security result", status); + this._rfb_init_state = "SecurityReason"; + this._security_context = "security result"; + this._security_status = status; + return this._init_msg(); } else { this.dispatchEvent(new CustomEvent( "securityfailure", @@ -1453,6 +1445,9 @@ export default class RFB extends EventTargetMixin { case 'SecurityResult': return this._handle_security_result(); + case 'SecurityReason': + return this._handle_security_reason(); + case 'ClientInitialisation': this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation this._rfb_init_state = 'ServerInitialisation'; From ee2423ed2b8df2955b470530409938a7fc63be5e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 10:25:50 +0100 Subject: [PATCH 021/207] Clean up RFB._rfb_auth_schema assignment --- kasmweb/core/rfb.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index e6427fd..d9d76e6 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -61,7 +61,7 @@ export default class RFB extends EventTargetMixin { // Internal state this._rfb_connection_state = ''; this._rfb_init_state = ''; - this._rfb_auth_scheme = ''; + this._rfb_auth_scheme = -1; this._rfb_clean_disconnect = true; // Server capabilities @@ -975,7 +975,6 @@ export default class RFB extends EventTargetMixin { Log.Debug("Server security types: " + types); // Look for each auth in preferred order - this._rfb_auth_scheme = 0; if (includes(1, types)) { this._rfb_auth_scheme = 1; // None } else if (includes(22, types)) { From 915901848d6f43bf5ce2e60bc6e08354bb49d77d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 10:26:27 +0100 Subject: [PATCH 022/207] Send data one byte at a time in tests This makes sure we don't have code assuming that everything is neatly packaged in a single WebSocket message. --- kasmweb/tests/fake.websocket.js | 7 ++++++- kasmweb/tests/test.rfb.js | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/kasmweb/tests/fake.websocket.js b/kasmweb/tests/fake.websocket.js index 2e1a3b9..68ab3f8 100644 --- a/kasmweb/tests/fake.websocket.js +++ b/kasmweb/tests/fake.websocket.js @@ -63,7 +63,12 @@ export default class FakeWebSocket { } _receive_data(data) { - this.onmessage(make_event("message", { 'data': data })); + // Break apart the data to expose bugs where we assume data is + // neatly packaged + for (let i = 0;i < data.length;i++) { + let buf = data.subarray(i, i+1); + this.onmessage(make_event("message", { 'data': buf })); + } } } diff --git a/kasmweb/tests/test.rfb.js b/kasmweb/tests/test.rfb.js index ca8a738..99c9c90 100644 --- a/kasmweb/tests/test.rfb.js +++ b/kasmweb/tests/test.rfb.js @@ -1039,14 +1039,14 @@ describe('Remote Frame Buffer Protocol Client', function () { const auth_scheme_raw = [1, 2, 3, 4]; const auth_scheme = (auth_scheme_raw[0] << 24) + (auth_scheme_raw[1] << 16) + (auth_scheme_raw[2] << 8) + auth_scheme_raw[3]; - client._sock._websocket._receive_data(auth_scheme_raw); + client._sock._websocket._receive_data(new Uint8Array(auth_scheme_raw)); expect(client._rfb_auth_scheme).to.equal(auth_scheme); }); it('should prefer no authentication is possible', function () { client._rfb_version = 3.7; const auth_schemes = [2, 1, 3]; - client._sock._websocket._receive_data(auth_schemes); + client._sock._websocket._receive_data(new Uint8Array(auth_schemes)); expect(client._rfb_auth_scheme).to.equal(1); expect(client._sock).to.have.sent(new Uint8Array([1, 1])); }); @@ -1054,7 +1054,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should choose for the most prefered scheme possible for versions >= 3.7', function () { client._rfb_version = 3.7; const auth_schemes = [2, 22, 16]; - client._sock._websocket._receive_data(auth_schemes); + client._sock._websocket._receive_data(new Uint8Array(auth_schemes)); expect(client._rfb_auth_scheme).to.equal(22); expect(client._sock).to.have.sent(new Uint8Array([22])); }); @@ -1063,7 +1063,7 @@ describe('Remote Frame Buffer Protocol Client', function () { sinon.spy(client, "_fail"); client._rfb_version = 3.7; const auth_schemes = [1, 32]; - client._sock._websocket._receive_data(auth_schemes); + client._sock._websocket._receive_data(new Uint8Array(auth_schemes)); expect(client._fail).to.have.been.calledOnce; }); @@ -1071,7 +1071,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfb_version = 3.7; const failure_data = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115]; sinon.spy(client, '_fail'); - client._sock._websocket._receive_data(failure_data); + client._sock._websocket._receive_data(new Uint8Array(failure_data)); expect(client._fail).to.have.been.calledOnce; expect(client._fail).to.have.been.calledWith( @@ -1082,7 +1082,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfb_version = 3.7; const auth_schemes = [1, 1]; client._negotiate_authentication = sinon.spy(); - client._sock._websocket._receive_data(auth_schemes); + client._sock._websocket._receive_data(new Uint8Array(auth_schemes)); expect(client._rfb_init_state).to.equal('Authentication'); expect(client._negotiate_authentication).to.have.been.calledOnce; }); @@ -1485,7 +1485,7 @@ describe('Remote Frame Buffer Protocol Client', function () { for (let i = 0; i < 16 + 32 + 48; i++) { tight_data.push(i); } - client._sock._websocket._receive_data(tight_data); + client._sock._websocket._receive_data(new Uint8Array(tight_data)); expect(client._rfb_connection_state).to.equal('connected'); }); @@ -2325,7 +2325,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should handle a message in the connected state as a normal message', function () { client._normal_msg = sinon.spy(); client._sock._websocket._receive_data(new Uint8Array([1, 2, 3])); - expect(client._normal_msg).to.have.been.calledOnce; + expect(client._normal_msg).to.have.been.called; }); it('should handle a message in any non-disconnected/failed state like an init message', function () { @@ -2333,7 +2333,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfb_init_state = 'ProtocolVersion'; client._init_msg = sinon.spy(); client._sock._websocket._receive_data(new Uint8Array([1, 2, 3])); - expect(client._init_msg).to.have.been.calledOnce; + expect(client._init_msg).to.have.been.called; }); it('should process all normal messages directly', function () { From d20f7514412518a82ec306f7ecc09fed1afa742e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 13:01:28 +0100 Subject: [PATCH 023/207] Throw correct Error object We've already defined the name Error as a logging function, so we need to be more explicit when we want to refer to the exception class. --- kasmweb/core/util/logging.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb/core/util/logging.js b/kasmweb/core/util/logging.js index 036a7dd..4c8943d 100644 --- a/kasmweb/core/util/logging.js +++ b/kasmweb/core/util/logging.js @@ -40,7 +40,7 @@ export function init_logging(level) { case 'none': break; default: - throw new Error("invalid logging type '" + level + "'"); + throw new window.Error("invalid logging type '" + level + "'"); } /* eslint-enable no-console, no-fallthrough */ } From 499b251716bd36ef25e0e92bb846559379142ca9 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 15 Feb 2019 13:14:36 +0100 Subject: [PATCH 024/207] Remove error handling in clientCutText() It is not necessary as Websock.flush() is guaranteed to succeed and give us some space. It also remove the call to _fail(), which was invalid at this place as clientCutText() is not a method on RFB. --- kasmweb/core/rfb.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index d9d76e6..8d87660 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -2024,11 +2024,6 @@ RFB.messages = { while (remaining > 0) { let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen)); - if (flushSize <= 0) { - this._fail("Clipboard contents could not be sent"); - break; - } - for (let i = 0; i < flushSize; i++) { buff[sock._sQlen + i] = text.charCodeAt(textOffset + i); } From 3043216b6318a9b4fa86caca73c6512911b1704d Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Sat, 16 Feb 2019 23:31:58 +0200 Subject: [PATCH 025/207] Remove intermediate variable from mouse --- kasmweb/core/input/mouse.js | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/kasmweb/core/input/mouse.js b/kasmweb/core/input/mouse.js index c78f2ab..58a2982 100644 --- a/kasmweb/core/input/mouse.js +++ b/kasmweb/core/input/mouse.js @@ -238,43 +238,39 @@ export default class Mouse { // ===== PUBLIC METHODS ===== grab() { - const c = this._target; - if (isTouchDevice) { - c.addEventListener('touchstart', this._eventHandlers.mousedown); - c.addEventListener('touchend', this._eventHandlers.mouseup); - c.addEventListener('touchmove', this._eventHandlers.mousemove); + this._target.addEventListener('touchstart', this._eventHandlers.mousedown); + this._target.addEventListener('touchend', this._eventHandlers.mouseup); + this._target.addEventListener('touchmove', this._eventHandlers.mousemove); } - c.addEventListener('mousedown', this._eventHandlers.mousedown); - c.addEventListener('mouseup', this._eventHandlers.mouseup); - c.addEventListener('mousemove', this._eventHandlers.mousemove); - c.addEventListener('wheel', this._eventHandlers.mousewheel); + this._target.addEventListener('mousedown', this._eventHandlers.mousedown); + this._target.addEventListener('mouseup', this._eventHandlers.mouseup); + this._target.addEventListener('mousemove', this._eventHandlers.mousemove); + this._target.addEventListener('wheel', this._eventHandlers.mousewheel); /* Prevent middle-click pasting (see above for why we bind to document) */ document.addEventListener('click', this._eventHandlers.mousedisable); /* preventDefault() on mousedown doesn't stop this event for some reason so we have to explicitly block it */ - c.addEventListener('contextmenu', this._eventHandlers.mousedisable); + this._target.addEventListener('contextmenu', this._eventHandlers.mousedisable); } ungrab() { - const c = this._target; - this._resetWheelStepTimers(); if (isTouchDevice) { - c.removeEventListener('touchstart', this._eventHandlers.mousedown); - c.removeEventListener('touchend', this._eventHandlers.mouseup); - c.removeEventListener('touchmove', this._eventHandlers.mousemove); + this._target.removeEventListener('touchstart', this._eventHandlers.mousedown); + this._target.removeEventListener('touchend', this._eventHandlers.mouseup); + this._target.removeEventListener('touchmove', this._eventHandlers.mousemove); } - c.removeEventListener('mousedown', this._eventHandlers.mousedown); - c.removeEventListener('mouseup', this._eventHandlers.mouseup); - c.removeEventListener('mousemove', this._eventHandlers.mousemove); - c.removeEventListener('wheel', this._eventHandlers.mousewheel); + this._target.removeEventListener('mousedown', this._eventHandlers.mousedown); + this._target.removeEventListener('mouseup', this._eventHandlers.mouseup); + this._target.removeEventListener('mousemove', this._eventHandlers.mousemove); + this._target.removeEventListener('wheel', this._eventHandlers.mousewheel); document.removeEventListener('click', this._eventHandlers.mousedisable); - c.removeEventListener('contextmenu', this._eventHandlers.mousedisable); + this._target.removeEventListener('contextmenu', this._eventHandlers.mousedisable); } } From 1ff792cf4b3e53ffdd7e7ebdeae5e7540616d48d Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Sat, 16 Feb 2019 23:33:49 +0200 Subject: [PATCH 026/207] Remove unnecessary constructor parameter from Cursor --- kasmweb/core/util/cursor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb/core/util/cursor.js b/kasmweb/core/util/cursor.js index b032ab6..b73f862 100644 --- a/kasmweb/core/util/cursor.js +++ b/kasmweb/core/util/cursor.js @@ -9,7 +9,7 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js'; const useFallback = !supportsCursorURIs() || isTouchDevice; export default class Cursor { - constructor(container) { + constructor() { this._target = null; this._canvas = document.createElement('canvas'); From 5964156a6f9830da96423ca593e9c313a5e742df Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Sat, 16 Feb 2019 23:34:13 +0200 Subject: [PATCH 027/207] Remove unnecessary context from eventtarget --- kasmweb/core/util/eventtarget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb/core/util/eventtarget.js b/kasmweb/core/util/eventtarget.js index d74ed28..f54ca9b 100644 --- a/kasmweb/core/util/eventtarget.js +++ b/kasmweb/core/util/eventtarget.js @@ -29,7 +29,7 @@ export default class EventTargetMixin { return true; } this._listeners.get(event.type) - .forEach(callback => callback.call(this, event), this); + .forEach(callback => callback.call(this, event)); return !event.defaultPrevented; } } From 7b7295fd4e11ab68fccea868b164efd813d235be Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Sat, 16 Feb 2019 23:36:16 +0200 Subject: [PATCH 028/207] Use default argument for base64 --- kasmweb/core/base64.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kasmweb/core/base64.js b/kasmweb/core/base64.js index 42ba53b..88e7454 100644 --- a/kasmweb/core/base64.js +++ b/kasmweb/core/base64.js @@ -56,9 +56,7 @@ export default { ], /* eslint-enable comma-spacing */ - decode(data, offset) { - offset = typeof(offset) !== 'undefined' ? offset : 0; - + decode(data, offset = 0) { let data_length = data.indexOf('=') - offset; if (data_length < 0) { data_length = data.length - offset; } From 075eed5cbb515e4625584d81013a97eaa2dce5f9 Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Sun, 17 Feb 2019 00:25:33 +0200 Subject: [PATCH 029/207] Convert DES into a class --- kasmweb/core/des.js | 141 ++++++++++++++++++++++---------------------- kasmweb/core/rfb.js | 7 +-- 2 files changed, 71 insertions(+), 77 deletions(-) diff --git a/kasmweb/core/des.js b/kasmweb/core/des.js index 175066b..d2f807b 100644 --- a/kasmweb/core/des.js +++ b/kasmweb/core/des.js @@ -77,67 +77,68 @@ /* eslint-disable comma-spacing */ -export default function DES(passwd) { - "use strict"; +// Tables, permutations, S-boxes, etc. +const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, + 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, + 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], + totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28]; - // Tables, permutations, S-boxes, etc. - const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, - 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, - 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], - totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], - z = 0x0, - keys = []; - let a,b,c,d,e,f; +const z = 0x0; +let a,b,c,d,e,f; +a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; +const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, + z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, + a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, + c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; +a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; +const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, + a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, + z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, + z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; +a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; +const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, + b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, + c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, + b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; +a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; +const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, + z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, + b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, + c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; +a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; +const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, + a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, + z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, + c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; +a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; +const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, + z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, + b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, + a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; +a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; +const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, + b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, + b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, + z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; +a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; +const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, + c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, + a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, + z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; - a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; - const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, - z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, - a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, - c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; - a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; - const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, - a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, - z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, - z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; - a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; - const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, - b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, - c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, - b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; - a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; - const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, - z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, - b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, - c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; - a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; - const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, - a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, - z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, - c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; - a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; - const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, - z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, - b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, - a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; - a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; - const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, - b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, - b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, - z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; - a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; - const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, - c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, - a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, - z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; +/* eslint-enable comma-spacing */ - // Set the key. - function setKeys(keyBlock) { +export default class DES { + constructor(password) { + this.keys = []; + + // Set the key. const pc1m = [], pcr = [], kn = []; for (let j = 0, l = 56; j < 56; ++j, l -= 8) { l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1 const m = l & 0x7; - pc1m[j] = ((keyBlock[l >>> 3] & (1<>> 3] & (1<>> 10; - keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; + this.keys[KnLi] = (raw0 & 0x00fc0000) << 6; + this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10; + this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10; + this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; ++KnLi; - keys[KnLi] = (raw0 & 0x0003f000) << 12; - keys[KnLi] |= (raw0 & 0x0000003f) << 16; - keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; - keys[KnLi] |= (raw1 & 0x0000003f); + this.keys[KnLi] = (raw0 & 0x0003f000) << 12; + this.keys[KnLi] |= (raw0 & 0x0000003f) << 16; + this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; + this.keys[KnLi] |= (raw1 & 0x0000003f); ++KnLi; } } // Encrypt 8 bytes of text - function enc8(text) { + enc8(text) { const b = text.slice(); let i = 0, l, r, x; // left, right, accumulator @@ -206,24 +207,24 @@ export default function DES(passwd) { for (let i = 0, keysi = 0; i < 8; ++i) { x = (r << 28) | (r >>> 4); - x ^= keys[keysi++]; + x ^= this.keys[keysi++]; let fval = SP7[x & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f]; - x = r ^ keys[keysi++]; + x = r ^ this.keys[keysi++]; fval |= SP8[x & 0x3f]; fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f]; fval |= SP2[(x >>> 24) & 0x3f]; l ^= fval; x = (l << 28) | (l >>> 4); - x ^= keys[keysi++]; + x ^= this.keys[keysi++]; fval = SP7[x & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f]; - x = l ^ keys[keysi++]; + x = l ^ this.keys[keysi++]; fval |= SP8[x & 0x0000003f]; fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f]; @@ -259,11 +260,7 @@ export default function DES(passwd) { } // Encrypt 16 bytes of text using passwd as key - function encrypt(t) { - return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16))); + encrypt(t) { + return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16))); } - - setKeys(passwd); // Setup keys - return {'encrypt': encrypt}; // Public interface - } diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index 8d87660..eb3f447 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -1914,11 +1914,8 @@ export default class RFB extends EventTargetMixin { } static genDES(password, challenge) { - const passwd = []; - for (let i = 0; i < password.length; i++) { - passwd.push(password.charCodeAt(i)); - } - return (new DES(passwd)).encrypt(challenge); + const passwordChars = password.split('').map(c => c.charCodeAt(0)); + return (new DES(passwordChars)).encrypt(challenge); } } From c755008d157b1031d7b9f3b4af67b0dd6dfee5c8 Mon Sep 17 00:00:00 2001 From: Dmitriy Shweew Date: Wed, 27 Feb 2019 04:24:22 +0400 Subject: [PATCH 030/207] Add Russian translation (#1211) By Dmitriy Shweew (shweew) --- kasmweb/app/locale/ru.json | 73 +++++++++ kasmweb/app/ui.js | 2 +- kasmweb/po/Makefile | 2 +- kasmweb/po/ru.po | 306 +++++++++++++++++++++++++++++++++++++ 4 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 kasmweb/app/locale/ru.json create mode 100644 kasmweb/po/ru.po diff --git a/kasmweb/app/locale/ru.json b/kasmweb/app/locale/ru.json new file mode 100644 index 0000000..52e57f3 --- /dev/null +++ b/kasmweb/app/locale/ru.json @@ -0,0 +1,73 @@ +{ + "Connecting...": "Подключение...", + "Disconnecting...": "Отключение...", + "Reconnecting...": "Переподключение...", + "Internal error": "Внутренняя ошибка", + "Must set host": "Задайте имя сервера или IP", + "Connected (encrypted) to ": "Подключено (с шифрованием) к ", + "Connected (unencrypted) to ": "Подключено (без шифрования) к ", + "Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано", + "Failed to connect to server": "Ошибка подключения к серверу", + "Disconnected": "Отключено", + "New connection has been rejected with reason: ": "Подключиться не удалось: ", + "New connection has been rejected": "Подключиться не удалось", + "Password is required": "Требуется пароль", + "noVNC encountered an error:": "Ошибка noVNC: ", + "Hide/Show the control bar": "Скрыть/Показать контрольную панель", + "Move/Drag Viewport": "Переместить окно", + "viewport drag": "Переместить окно", + "Active Mouse Button": "Активировать кнопки мыши", + "No mousebutton": "Отключить кнопки мыши", + "Left mousebutton": "Левая кнопка мыши", + "Middle mousebutton": "Средняя кнопка мыши", + "Right mousebutton": "Правая кнопка мыши", + "Keyboard": "Клавиатура", + "Show Keyboard": "Показать клавиатуру", + "Extra keys": "Доп. кнопки", + "Show Extra Keys": "Показать дополнительные кнопки", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Передать нажатие Ctrl", + "Alt": "Alt", + "Toggle Alt": "Передать нажатие Alt", + "Toggle Windows": "Переключение вкладок", + "Windows": "Вкладка", + "Send Tab": "Передать нажатие Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Передать нажатие Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del", + "Shutdown/Reboot": "Выключить/Перезагрузить", + "Shutdown/Reboot...": "Выключить/Перезагрузить...", + "Power": "Питание", + "Shutdown": "Выключить", + "Reboot": "Перезагрузить", + "Reset": "Сброс", + "Clipboard": "Буфер обмена", + "Clear": "Очистить", + "Fullscreen": "Во весь экран", + "Settings": "Настройки", + "Shared Mode": "Общий режим", + "View Only": "Просмотр", + "Clip to Window": "В окно", + "Scaling Mode:": "Масштаб:", + "None": "Нет", + "Local Scaling": "Локльный масштаб", + "Remote Resizing": "Удаленный масштаб", + "Advanced": "Дополнительно", + "Repeater ID:": "Идентификатор ID:", + "WebSocket": "WebSocket", + "Encrypt": "Шифрование", + "Host:": "Сервер:", + "Port:": "Порт:", + "Path:": "Путь:", + "Automatic Reconnect": "Автоматическое переподключение", + "Reconnect Delay (ms):": "Задержка переподключения (мс):", + "Show Dot when No Cursor": "Показать точку вместо курсора", + "Logging:": "Лог:", + "Disconnect": "Отключение", + "Connect": "Подключение", + "Password:": "Пароль:", + "Send Password": "Пароль: ", + "Cancel": "Выход" +} \ No newline at end of file diff --git a/kasmweb/app/ui.js b/kasmweb/app/ui.js index 1e2d7a6..4f5a17e 100644 --- a/kasmweb/app/ui.js +++ b/kasmweb/app/ui.js @@ -2015,7 +2015,7 @@ const UI = { }; // Set up translations -const LINGUAS = ["cs", "de", "el", "es", "ko", "nl", "pl", "sv", "tr", "zh_CN", "zh_TW"]; +const LINGUAS = ["cs", "de", "el", "es", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"]; l10n.setup(LINGUAS); if (l10n.language !== "en" && l10n.dictionary === undefined) { WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', (translations) => { diff --git a/kasmweb/po/Makefile b/kasmweb/po/Makefile index f9756bd..6dbd830 100644 --- a/kasmweb/po/Makefile +++ b/kasmweb/po/Makefile @@ -1,7 +1,7 @@ all: .PHONY: update-po update-js update-pot -LINGUAS := cs de el es ko nl pl sv tr zh_CN zh_TW +LINGUAS := cs de el es ko nl pl ru sv tr zh_CN zh_TW VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4) diff --git a/kasmweb/po/ru.po b/kasmweb/po/ru.po new file mode 100644 index 0000000..fb5d087 --- /dev/null +++ b/kasmweb/po/ru.po @@ -0,0 +1,306 @@ +# Russian translations for noVNC package +# Русский перевод для пакета noVNC. +# Copyright (C) 2019 Dmitriy Shweew +# This file is distributed under the same license as the noVNC package. +# Dmitriy Shweew , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: noVNC 1.1.0\n" +"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" +"POT-Creation-Date: 2019-02-26 14:53+0400\n" +"PO-Revision-Date: 2019-02-17 17:29+0400\n" +"Last-Translator: Dmitriy Shweew \n" +"Language-Team: Russian\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 2.2.1\n" +"X-Poedit-Flags-xgettext: --add-comments\n" + +#: ../app/ui.js:387 +msgid "Connecting..." +msgstr "Подключение..." + +#: ../app/ui.js:394 +msgid "Disconnecting..." +msgstr "Отключение..." + +#: ../app/ui.js:400 +msgid "Reconnecting..." +msgstr "Переподключение..." + +#: ../app/ui.js:405 +msgid "Internal error" +msgstr "Внутренняя ошибка" + +#: ../app/ui.js:995 +msgid "Must set host" +msgstr "Задайте имя сервера или IP" + +#: ../app/ui.js:1077 +msgid "Connected (encrypted) to " +msgstr "Подключено (с шифрованием) к " + +#: ../app/ui.js:1079 +msgid "Connected (unencrypted) to " +msgstr "Подключено (без шифрования) к " + +#: ../app/ui.js:1102 +msgid "Something went wrong, connection is closed" +msgstr "Что-то пошло не так, подключение разорвано" + +#: ../app/ui.js:1105 +msgid "Failed to connect to server" +msgstr "Ошибка подключения к серверу" + +#: ../app/ui.js:1115 +msgid "Disconnected" +msgstr "Отключено" + +#: ../app/ui.js:1128 +msgid "New connection has been rejected with reason: " +msgstr "Подключиться не удалось: " + +#: ../app/ui.js:1131 +msgid "New connection has been rejected" +msgstr "Подключиться не удалось" + +#: ../app/ui.js:1151 +msgid "Password is required" +msgstr "Требуется пароль" + +#: ../vnc.html:84 +msgid "noVNC encountered an error:" +msgstr "Ошибка noVNC: " + +#: ../vnc.html:94 +msgid "Hide/Show the control bar" +msgstr "Скрыть/Показать контрольную панель" + +#: ../vnc.html:101 +msgid "Move/Drag Viewport" +msgstr "Переместить окно" + +#: ../vnc.html:101 +msgid "viewport drag" +msgstr "Переместить окно" + +#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116 +msgid "Active Mouse Button" +msgstr "Активировать кнопки мыши" + +#: ../vnc.html:107 +msgid "No mousebutton" +msgstr "Отключить кнопки мыши" + +#: ../vnc.html:110 +msgid "Left mousebutton" +msgstr "Левая кнопка мыши" + +#: ../vnc.html:113 +msgid "Middle mousebutton" +msgstr "Средняя кнопка мыши" + +#: ../vnc.html:116 +msgid "Right mousebutton" +msgstr "Правая кнопка мыши" + +#: ../vnc.html:119 +msgid "Keyboard" +msgstr "Клавиатура" + +#: ../vnc.html:119 +msgid "Show Keyboard" +msgstr "Показать клавиатуру" + +#: ../vnc.html:126 +msgid "Extra keys" +msgstr "Доп. кнопки" + +#: ../vnc.html:126 +msgid "Show Extra Keys" +msgstr "Показать дополнительные кнопки" + +#: ../vnc.html:131 +msgid "Ctrl" +msgstr "Ctrl" + +#: ../vnc.html:131 +msgid "Toggle Ctrl" +msgstr "Передать нажатие Ctrl" + +#: ../vnc.html:134 +msgid "Alt" +msgstr "Alt" + +#: ../vnc.html:134 +msgid "Toggle Alt" +msgstr "Передать нажатие Alt" + +#: ../vnc.html:137 +msgid "Toggle Windows" +msgstr "Переключение вкладок" + +#: ../vnc.html:137 +msgid "Windows" +msgstr "Вкладка" + +#: ../vnc.html:140 +msgid "Send Tab" +msgstr "Передать нажатие Tab" + +#: ../vnc.html:140 +msgid "Tab" +msgstr "Tab" + +#: ../vnc.html:143 +msgid "Esc" +msgstr "Esc" + +#: ../vnc.html:143 +msgid "Send Escape" +msgstr "Передать нажатие Escape" + +#: ../vnc.html:146 +msgid "Ctrl+Alt+Del" +msgstr "Ctrl+Alt+Del" + +#: ../vnc.html:146 +msgid "Send Ctrl-Alt-Del" +msgstr "Передать нажатие Ctrl-Alt-Del" + +#: ../vnc.html:154 +msgid "Shutdown/Reboot" +msgstr "Выключить/Перезагрузить" + +#: ../vnc.html:154 +msgid "Shutdown/Reboot..." +msgstr "Выключить/Перезагрузить..." + +#: ../vnc.html:160 +msgid "Power" +msgstr "Питание" + +#: ../vnc.html:162 +msgid "Shutdown" +msgstr "Выключить" + +#: ../vnc.html:163 +msgid "Reboot" +msgstr "Перезагрузить" + +#: ../vnc.html:164 +msgid "Reset" +msgstr "Сброс" + +#: ../vnc.html:169 ../vnc.html:175 +msgid "Clipboard" +msgstr "Буфер обмена" + +#: ../vnc.html:179 +msgid "Clear" +msgstr "Очистить" + +#: ../vnc.html:185 +msgid "Fullscreen" +msgstr "Во весь экран" + +#: ../vnc.html:190 ../vnc.html:197 +msgid "Settings" +msgstr "Настройки" + +#: ../vnc.html:200 +msgid "Shared Mode" +msgstr "Общий режим" + +#: ../vnc.html:203 +msgid "View Only" +msgstr "Просмотр" + +#: ../vnc.html:207 +msgid "Clip to Window" +msgstr "В окно" + +#: ../vnc.html:210 +msgid "Scaling Mode:" +msgstr "Масштаб:" + +#: ../vnc.html:212 +msgid "None" +msgstr "Нет" + +#: ../vnc.html:213 +msgid "Local Scaling" +msgstr "Локльный масштаб" + +#: ../vnc.html:214 +msgid "Remote Resizing" +msgstr "Удаленный масштаб" + +#: ../vnc.html:219 +msgid "Advanced" +msgstr "Дополнительно" + +#: ../vnc.html:222 +msgid "Repeater ID:" +msgstr "Идентификатор ID:" + +#: ../vnc.html:226 +msgid "WebSocket" +msgstr "WebSocket" + +#: ../vnc.html:229 +msgid "Encrypt" +msgstr "Шифрование" + +#: ../vnc.html:232 +msgid "Host:" +msgstr "Сервер:" + +#: ../vnc.html:236 +msgid "Port:" +msgstr "Порт:" + +#: ../vnc.html:240 +msgid "Path:" +msgstr "Путь:" + +#: ../vnc.html:247 +msgid "Automatic Reconnect" +msgstr "Автоматическое переподключение" + +#: ../vnc.html:250 +msgid "Reconnect Delay (ms):" +msgstr "Задержка переподключения (мс):" + +#: ../vnc.html:255 +msgid "Show Dot when No Cursor" +msgstr "Показать точку вместо курсора" + +#: ../vnc.html:260 +msgid "Logging:" +msgstr "Лог:" + +#: ../vnc.html:272 +msgid "Disconnect" +msgstr "Отключение" + +#: ../vnc.html:291 +msgid "Connect" +msgstr "Подключение" + +#: ../vnc.html:301 +msgid "Password:" +msgstr "Пароль:" + +#: ../vnc.html:305 +msgid "Send Password" +msgstr "Пароль: " + +#: ../vnc.html:315 +msgid "Cancel" +msgstr "Выход" From 37c17ddbf9326de1731678ea2dd934719f1817b5 Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Wed, 27 Feb 2019 10:13:50 +0200 Subject: [PATCH 031/207] Move support check from display to browser --- kasmweb/core/display.js | 11 ++--------- kasmweb/core/util/browser.js | 9 +++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/kasmweb/core/display.js b/kasmweb/core/display.js index 041daee..acc1878 100644 --- a/kasmweb/core/display.js +++ b/kasmweb/core/display.js @@ -8,14 +8,7 @@ import * as Log from './util/logging.js'; import Base64 from "./base64.js"; - -let SUPPORTS_IMAGEDATA_CONSTRUCTOR = false; -try { - new ImageData(new Uint8ClampedArray(4), 1, 1); - SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; -} catch (ex) { - // ignore failure -} +import { supportsImageMetadata } from './util/browser.js'; export default class Display { constructor(target) { @@ -581,7 +574,7 @@ export default class Display { _rgbxImageData(x, y, width, height, arr, offset) { // NB(directxman12): arr must be an Type Array view let img; - if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { + if (supportsImageMetadata) { img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height); } else { img = this._drawCtx.createImageData(width, height); diff --git a/kasmweb/core/util/browser.js b/kasmweb/core/util/browser.js index 026a31a..78e104d 100644 --- a/kasmweb/core/util/browser.js +++ b/kasmweb/core/util/browser.js @@ -49,6 +49,15 @@ export function supportsCursorURIs() { return _cursor_uris_supported; } +let _supportsImageMetadata = false; +try { + new ImageData(new Uint8ClampedArray(4), 1, 1); + _supportsImageMetadata = true; +} catch (ex) { + // ignore failure +} +export const supportsImageMetadata = _supportsImageMetadata; + export function isMac() { return navigator && !!(/mac/i).exec(navigator.platform); } From c6c278f9d5f1b3f35ae6c0e79cca3a94aa2660a2 Mon Sep 17 00:00:00 2001 From: Juanjo Diaz Date: Wed, 27 Feb 2019 10:14:50 +0200 Subject: [PATCH 032/207] Replace unnecessary function supportsCursorURIs by a constant variable --- kasmweb/core/util/browser.js | 32 +++++++++++++------------------- kasmweb/core/util/cursor.js | 2 +- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/kasmweb/core/util/browser.js b/kasmweb/core/util/browser.js index 78e104d..8996cfe 100644 --- a/kasmweb/core/util/browser.js +++ b/kasmweb/core/util/browser.js @@ -25,30 +25,24 @@ window.addEventListener('touchstart', function onFirstTouch() { // brings us a bit closer but is not optimal. export let dragThreshold = 10 * (window.devicePixelRatio || 1); -let _cursor_uris_supported = null; +let _supportsCursorURIs = false; -export function supportsCursorURIs() { - if (_cursor_uris_supported === null) { - try { - const target = document.createElement('canvas'); - target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default'; +try { + const target = document.createElement('canvas'); + target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default'; - if (target.style.cursor) { - Log.Info("Data URI scheme cursor supported"); - _cursor_uris_supported = true; - } else { - Log.Warn("Data URI scheme cursor not supported"); - _cursor_uris_supported = false; - } - } catch (exc) { - Log.Error("Data URI scheme cursor test exception: " + exc); - _cursor_uris_supported = false; - } + if (target.style.cursor) { + Log.Info("Data URI scheme cursor supported"); + _supportsCursorURIs = true; + } else { + Log.Warn("Data URI scheme cursor not supported"); } - - return _cursor_uris_supported; +} catch (exc) { + Log.Error("Data URI scheme cursor test exception: " + exc); } +export const supportsCursorURIs = _supportsCursorURIs; + let _supportsImageMetadata = false; try { new ImageData(new Uint8ClampedArray(4), 1, 1); diff --git a/kasmweb/core/util/cursor.js b/kasmweb/core/util/cursor.js index b73f862..0d0b754 100644 --- a/kasmweb/core/util/cursor.js +++ b/kasmweb/core/util/cursor.js @@ -6,7 +6,7 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js'; -const useFallback = !supportsCursorURIs() || isTouchDevice; +const useFallback = !supportsCursorURIs || isTouchDevice; export default class Cursor { constructor() { From 79ab05192b95ffa679b4188b05fc9bc28c121182 Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Fri, 8 Mar 2019 16:30:43 +0100 Subject: [PATCH 033/207] Add check for bad values for Display.autoscale() --- kasmweb/core/display.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kasmweb/core/display.js b/kasmweb/core/display.js index acc1878..4df9d17 100644 --- a/kasmweb/core/display.js +++ b/kasmweb/core/display.js @@ -503,6 +503,11 @@ export default class Display { } autoscale(containerWidth, containerHeight) { + if (containerWidth === 0 || containerHeight === 0) { + Log.Warn("Autoscale doesn't work when width or height is zero"); + return; + } + const vp = this._viewportLoc; const targetAspectRatio = containerWidth / containerHeight; const fbAspectRatio = vp.w / vp.h; From 50183c7caa8af53ad45baa3dd707f9ccc18ea571 Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Wed, 13 Mar 2019 14:19:51 +0100 Subject: [PATCH 034/207] Add default language --- kasmweb/vnc.html | 2 +- kasmweb/vnc_lite.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kasmweb/vnc.html b/kasmweb/vnc.html index bce252b..866ab62 100644 --- a/kasmweb/vnc.html +++ b/kasmweb/vnc.html @@ -1,5 +1,5 @@ - + From d1fdb877ef0cc681319410704e2e61a100722b23 Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Wed, 13 Mar 2019 14:31:17 +0100 Subject: [PATCH 036/207] Fix invalid input type 'input' --- kasmweb/vnc.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kasmweb/vnc.html b/kasmweb/vnc.html index 2c41a56..e1f33d7 100644 --- a/kasmweb/vnc.html +++ b/kasmweb/vnc.html @@ -263,7 +263,7 @@
  • - +
  • WebSocket
    @@ -281,7 +281,7 @@
  • - +
From 0045f0796535b283c8474a8b7c0c113b8efb1de2 Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Wed, 13 Mar 2019 14:36:23 +0100 Subject: [PATCH 037/207] Remove invalid HTML attributes from textarea --- kasmweb/vnc.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kasmweb/vnc.html b/kasmweb/vnc.html index e1f33d7..1c2e065 100644 --- a/kasmweb/vnc.html +++ b/kasmweb/vnc.html @@ -367,8 +367,7 @@ on-screen keyboard. Let's hope Chrome implements the ime-mode style for example --> + autocomplete="off" spellcheck="false" tabindex="-1">