Skip to content
Commits on Source (6)
tigervnc-tags
*.[ao]
*.mo
*.la
*.lo
.deps
.libs
CMakeFiles
CMakeCache.txt
*.cmake
Makefile
Makefile.in
config.h
language:
- c++
- java
# Travis is still stuck on Ubuntu 14.04, which has a too old FLTK
before_install:
- wget http://fltk.org/pub/fltk/1.3.4/fltk-1.3.4-2-source.tar.gz
- tar -xvf fltk-1.3.4-2-source.tar.gz
- pushd fltk-1.3.4-2
- ./configure --prefix=/usr --enable-shared
- make -j2
- sudo make install
- popd
script:
- cmake . && make
- cd java && cmake . && make
......@@ -158,8 +158,8 @@ The following procedure will build both the TigerVNC Viewer and a
"legacy-friendly" version of the TigerVNC Server:
cd {build_directory}
sh {source_directory}/contrib/xorg/build-xorg init
sh {source_directory}/contrib/xorg/build-xorg build [additional CMake flags]
bash {source_directory}/contrib/xorg/build-xorg init
bash {source_directory}/contrib/xorg/build-xorg build [additional CMake flags]
build-xorg generates a version of Xvnc that has no external dependencies on the
X11 shared libraries or any other distribution-specific shared libraries. This
......
......@@ -2,7 +2,7 @@
# Setup
#
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 2.8.11)
if(POLICY CMP0022)
cmake_policy(SET CMP0022 OLD)
endif()
......@@ -21,10 +21,10 @@ include(CheckCSourceRuns)
include(CMakeMacroLibtoolFile)
project(tigervnc)
set(VERSION 1.9.0)
set(VERSION 1.10.0)
# The RC version must always be four comma-separated numbers
set(RCVERSION 1,9,0,0)
set(RCVERSION 1,10,0,0)
# Installation paths
set(BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin")
......@@ -43,9 +43,7 @@ if(MSVC)
endif()
if(NOT BUILD_TIMESTAMP)
set(BUILD_TIMESTAMP "")
execute_process(COMMAND "date" "+%Y-%m-%d %H:%M" OUTPUT_VARIABLE BUILD_TIMESTAMP)
string(REGEX REPLACE "\n" "" BUILD_TIMESTAMP ${BUILD_TIMESTAMP})
STRING(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M" UTC)
endif()
# Default to optimised builds instead of debug ones. Our code has no bugs ;)
......@@ -76,8 +74,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wformat=2")
# Make sure we catch these issues whilst developing
IF(CMAKE_BUILD_TYPE MATCHES Debug)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Werror=vla")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Werror=vla")
ENDIF()
option(ENABLE_ASAN "Enable address sanitizer support" OFF)
......
......@@ -34,7 +34,7 @@ Incomplete and generally out of date copyright list::
Copyright (C) 2009-2011 D. R. Commander
Copyright (C) 2009-2011 Pierre Ossman for Cendio AB
Copyright (C) 2004, 2009-2011 Red Hat, Inc.
Copyright (C) 2009-2018 TigerVNC Team
Copyright (C) 2009-2019 TigerVNC Team
All Rights Reserved.
This software is distributed under the GNU General Public Licence as published
......
......@@ -19,10 +19,7 @@ if(BUILD_STATIC)
set(BUILD_STATIC_GCC 1)
set(JPEG_LIBRARIES "-Wl,-Bstatic -ljpeg -Wl,-Bdynamic")
if(WIN32)
set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic")
endif()
set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic")
# gettext is included in libc on many unix systems
if(NOT LIBC_HAS_DGETTEXT)
......@@ -91,7 +88,7 @@ if(BUILD_STATIC)
if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} ${X11_Xcursor_LIB} ${X11_Xfixes_LIB} -Wl,-Bstatic -lXft -Wl,-Bdynamic -lfontconfig -lXrender -lXext -R/usr/sfw/lib -L=/usr/sfw/lib -lfreetype -lsocket -lnsl")
else()
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -Wl,-Bstatic -lXcursor -lXfixes -lXft -lfontconfig -lexpat -lfreetype -lpng -lbz2 -lXrender -lXext -lXinerama -Wl,-Bdynamic")
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -Wl,-Bstatic -lXcursor -lXfixes -lXft -lfontconfig -lexpat -lfreetype -lpng -lbz2 -luuid -lXrender -lXext -lXinerama -Wl,-Bdynamic")
endif()
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -lX11")
......
......@@ -144,15 +144,6 @@ namespace network {
// This is only necessary if the Socket has been put in non-blocking
// mode and needs this callback to flush the buffer.
virtual void processSocketWriteEvent(network::Socket* sock) = 0;
// checkTimeouts() allows the server to check socket timeouts, etc. The
// return value is the number of milliseconds to wait before
// checkTimeouts() should be called again. If this number is zero then
// there is no timeout and checkTimeouts() should be called the next time
// an event occurs.
virtual int checkTimeouts() = 0;
virtual bool getDisable() {return false;};
};
}
......
......@@ -736,7 +736,7 @@ char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
strcat(buffer, "]");
addr.buf = rfb::strDup(buffer);
} else if (p.address.u.sa.sa_family == AF_UNSPEC)
} else
addr.buf = rfb::strDup("");
char action;
......
......@@ -215,20 +215,10 @@ int FdInStream::readWithTimeoutOrCallback(void* buf, int len, bool wait)
if (timing) {
gettimeofday(&after, 0);
// fprintf(stderr,"%d.%06d\n",(after.tv_sec - before.tv_sec),
// (after.tv_usec - before.tv_usec));
int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
(after.tv_usec - before.tv_usec) / 100);
int newKbits = n * 8 / 1000;
// if (newTimeWaited == 0) {
// fprintf(stderr,"new kbps infinite t %d k %d\n",
// newTimeWaited, newKbits);
// } else {
// fprintf(stderr,"new kbps %d t %d k %d\n",
// newKbits * 10000 / newTimeWaited, newTimeWaited, newKbits);
// }
// limit rate to between 10kbit/s and 40Mbit/s
if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
......
......@@ -18,6 +18,7 @@
#include <rdr/RandomStream.h>
#include <rdr/Exception.h>
#include <rfb/LogWriter.h>
#include <time.h>
#include <stdlib.h>
#ifndef WIN32
......@@ -30,6 +31,8 @@
#endif
#endif
static rfb::LogWriter vlog("RandomStream");
using namespace rdr;
const int DEFAULT_BUF_LEN = 256;
......@@ -46,11 +49,11 @@ RandomStream::RandomStream()
if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
if (GetLastError() == (DWORD)NTE_BAD_KEYSET) {
if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
fprintf(stderr, "RandomStream: unable to create keyset\n");
vlog.error("unable to create keyset");
provider = 0;
}
} else {
fprintf(stderr, "RandomStream: unable to acquire context\n");
vlog.error("unable to acquire context");
provider = 0;
}
}
......@@ -65,7 +68,7 @@ RandomStream::RandomStream()
{
#endif
#endif
fprintf(stderr,"RandomStream: warning: no OS supplied random source - using rand()\n");
vlog.error("no OS supplied random source - using rand()");
seed += (unsigned int) time(0) + getpid() + getpid() * 987654 + rand();
srand(seed);
}
......
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RDR_SUBSTITUTINGINSTREAM_H__
#define __RDR_SUBSTITUTINGINSTREAM_H__
#include <rdr/InStream.h>
#include <rdr/Exception.h>
namespace rdr {
class Substitutor {
public:
virtual char* substitute(const char* varName) = 0;
};
class SubstitutingInStream : public InStream {
public:
SubstitutingInStream(InStream* underlying_, Substitutor* s,
int maxVarNameLen_)
: underlying(underlying_), dollar(0), substitutor(s), subst(0),
maxVarNameLen(maxVarNameLen_)
{
ptr = end = underlying->getptr();
varName = new char[maxVarNameLen+1];
}
~SubstitutingInStream() {
delete underlying;
delete [] varName;
delete [] subst;
}
int pos() { return underlying->pos(); }
virtual int overrun(int itemSize, int nItems, bool wait=true) {
if (itemSize != 1)
throw new rdr::Exception("SubstitutingInStream: itemSize must be 1");
if (subst) {
delete [] subst;
subst = 0;
} else {
underlying->setptr(ptr);
}
underlying->check(1);
ptr = underlying->getptr();
end = underlying->getend();
dollar = (const U8*)memchr(ptr, '$', end-ptr);
if (dollar) {
if (dollar == ptr) {
try {
int i = 0;
while (i < maxVarNameLen) {
varName[i++] = underlying->readS8();
varName[i] = 0;
subst = substitutor->substitute(varName);
if (subst) {
ptr = (U8*)subst;
end = (U8*)subst + strlen(subst);
break;
}
}
} catch (EndOfStream&) {
}
if (!subst)
dollar = (const U8*)memchr(ptr+1, '$', end-ptr-1);
}
if (!subst && dollar) end = dollar;
}
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
InStream* underlying;
const U8* dollar;
Substitutor* substitutor;
char* varName;
char* subst;
int maxVarNameLen;
};
}
#endif
......@@ -21,11 +21,14 @@
#include <rdr/ZlibOutStream.h>
#include <rdr/Exception.h>
#include <rfb/LogWriter.h>
#include <zlib.h>
#undef ZLIBOUT_DEBUG
static rfb::LogWriter vlog("ZlibOutStream");
using namespace rdr;
enum { DEFAULT_BUF_SIZE = 16384 };
......@@ -85,7 +88,7 @@ void ZlibOutStream::flush()
zs->avail_in = ptr - start;
#ifdef ZLIBOUT_DEBUG
fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
vlog.debug("flush: avail_in %d",zs->avail_in);
#endif
// Force out everything from the zlib encoder
......@@ -98,7 +101,7 @@ void ZlibOutStream::flush()
int ZlibOutStream::overrun(int itemSize, int nItems)
{
#ifdef ZLIBOUT_DEBUG
fprintf(stderr,"zos overrun\n");
vlog.debug("overrun");
#endif
if (itemSize > bufSize)
......@@ -120,7 +123,7 @@ int ZlibOutStream::overrun(int itemSize, int nItems)
} else {
// but didn't consume all the data? try shifting what's left to the
// start of the buffer.
fprintf(stderr,"z out buf not full, but in data not consumed\n");
vlog.info("z out buf not full, but in data not consumed");
memmove(start, zs->next_in, ptr - zs->next_in);
offset += zs->next_in - start;
ptr -= zs->next_in - start;
......@@ -149,8 +152,8 @@ void ZlibOutStream::deflate(int flush)
zs->avail_out = underlying->getend() - underlying->getptr();
#ifdef ZLIBOUT_DEBUG
fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
zs->avail_in,zs->avail_out);
vlog.debug("calling deflate, avail_in %d, avail_out %d",
zs->avail_in,zs->avail_out);
#endif
rc = ::deflate(zs, flush);
......@@ -163,8 +166,8 @@ void ZlibOutStream::deflate(int flush)
}
#ifdef ZLIBOUT_DEBUG
fprintf(stderr,"zos: after deflate: %d bytes\n",
zs->next_out-underlying->getptr());
vlog.debug("after deflate: %d bytes",
zs->next_out-underlying->getptr());
#endif
underlying->setptr(zs->next_out);
......@@ -177,7 +180,7 @@ void ZlibOutStream::checkCompressionLevel()
if (newLevel != compressionLevel) {
#ifdef ZLIBOUT_DEBUG
fprintf(stderr,"zos change: avail_in %d\n",zs->avail_in);
vlog.debug("change: avail_in %d",zs->avail_in);
#endif
// zlib is just horribly stupid. It does an implicit flush on
......
......@@ -20,14 +20,20 @@
using namespace rfb;
IntParameter Blacklist::threshold("BlacklistThreshold",
"The number of unauthenticated connection attempts allowed from any "
"individual host before that host is black-listed",
5);
IntParameter Blacklist::initialTimeout("BlacklistTimeout",
"The initial timeout applied when a host is first black-listed. "
"The host cannot re-attempt a connection until the timeout expires.",
10);
BoolParameter enabled("UseBlacklist",
"Temporarily reject connections from a host if it "
"repeatedly fails to authenticate.",
true);
IntParameter threshold("BlacklistThreshold",
"The number of unauthenticated connection attempts "
"allowed from any individual host before that host "
"is black-listed",
5);
IntParameter initialTimeout("BlacklistTimeout",
"The initial timeout applied when a host is "
"first black-listed. The host cannot re-attempt "
"a connection until the timeout expires.",
10);
Blacklist::Blacklist() {
......@@ -42,6 +48,9 @@ Blacklist::~Blacklist() {
}
bool Blacklist::isBlackmarked(const char* name) {
if (!enabled)
return false;
BlacklistMap::iterator i = blm.find(name);
if (i == blm.end()) {
// Entry is not already black-marked.
......
......@@ -67,9 +67,6 @@ namespace rfb {
bool isBlackmarked(const char* name);
void clearBlackmark(const char* name);
static IntParameter threshold;
static IntParameter initialTimeout;
protected:
struct ltStr {
bool operator()(const char* s1, const char* s2) const {
......
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2017 Pierre Ossman for Cendio AB
* Copyright 2011-2019 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
......@@ -16,14 +16,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <rfb/Exception.h>
#include <rfb/clipboardTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h>
#include <rfb/CSecurity.h>
#include <rfb/Decoder.h>
#include <rfb/Security.h>
#include <rfb/SecurityClient.h>
#include <rfb/CConnection.h>
......@@ -39,21 +42,32 @@ using namespace rfb;
static LogWriter vlog("CConnection");
CConnection::CConnection()
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
: csecurity(0),
supportsLocalCursor(false), supportsDesktopResize(false),
supportsLEDState(false),
is(0), os(0), reader_(0), writer_(0),
shared(false),
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
framebuffer(NULL), decoder(this)
state_(RFBSTATE_UNINITIALISED),
pendingPFChange(false), preferredEncoding(encodingTight),
compressLevel(2), qualityLevel(-1),
formatChange(false), encodingChange(false),
firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
forceNonincremental(true),
framebuffer(NULL), decoder(this),
serverClipboard(NULL), hasLocalClipboard(false)
{
}
CConnection::~CConnection()
{
setFramebuffer(NULL);
if (csecurity) csecurity->destroy();
if (csecurity)
delete csecurity;
delete reader_;
reader_ = 0;
delete writer_;
writer_ = 0;
strFree(serverClipboard);
}
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
......@@ -66,6 +80,11 @@ void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
{
decoder.flush();
if (fb) {
assert(fb->width() == server.width());
assert(fb->height() == server.height());
}
if ((framebuffer != NULL) && (fb != NULL)) {
Rect rect;
......@@ -127,35 +146,51 @@ void CConnection::processMsg()
void CConnection::processVersionMsg()
{
char verStr[27]; // FIXME: gcc has some bug in format-overflow
int majorVersion;
int minorVersion;
vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {
if (!is->checkNoWait(12))
return;
is->readBytes(verStr, 12);
verStr[12] = '\0';
if (sscanf(verStr, "RFB %03d.%03d\n",
&majorVersion, &minorVersion) != 2) {
state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB server?");
}
if (!done) return;
server.setVersion(majorVersion, minorVersion);
vlog.info("Server supports RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);
// The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
if (cp.beforeVersion(3,3)) {
if (server.beforeVersion(3,3)) {
vlog.error("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);
state_ = RFBSTATE_INVALID;
throw Exception("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
} else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
cp.setVersion(3,3);
} else if (cp.afterVersion(3,8)) {
cp.setVersion(3,8);
server.majorVersion, server.minorVersion);
} else if (server.beforeVersion(3,7)) {
server.setVersion(3,3);
} else if (server.afterVersion(3,8)) {
server.setVersion(3,8);
}
cp.writeVersion(os);
sprintf(verStr, "RFB %03d.%03d\n",
server.majorVersion, server.minorVersion);
os->writeBytes(verStr, 12);
os->flush();
state_ = RFBSTATE_SECURITY_TYPES;
vlog.info("Using RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);
}
......@@ -168,7 +203,7 @@ void CConnection::processSecurityTypesMsg()
std::list<rdr::U8> secTypes;
secTypes = security.GetEnabledSecTypes();
if (cp.isVersion(3,3)) {
if (server.isVersion(3,3)) {
// legacy 3.3 server may only offer "vnc authentication" or "none"
......@@ -234,14 +269,14 @@ void CConnection::processSecurityTypesMsg()
}
state_ = RFBSTATE_SECURITY;
csecurity = security.GetCSecurity(secType);
csecurity = security.GetCSecurity(this, secType);
processSecurityMsg();
}
void CConnection::processSecurityMsg()
{
vlog.debug("processing security message");
if (csecurity->processMsg(this)) {
if (csecurity->processMsg()) {
state_ = RFBSTATE_SECURITY_RESULT;
processSecurityResultMsg();
}
......@@ -251,7 +286,7 @@ void CConnection::processSecurityResultMsg()
{
vlog.debug("processing security result message");
int result;
if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
result = secResultOK;
} else {
if (!is->checkNoWait(1)) return;
......@@ -271,7 +306,7 @@ void CConnection::processSecurityResultMsg()
throw Exception("Unknown security result from server");
}
state_ = RFBSTATE_INVALID;
if (cp.beforeVersion(3,8))
if (server.beforeVersion(3,8))
throw AuthFailureException();
CharArray reason(is->readString());
throw AuthFailureException(reason.buf);
......@@ -295,7 +330,7 @@ void CConnection::securityCompleted()
{
state_ = RFBSTATE_INITIALISATION;
reader_ = new CMsgReader(this, is);
writer_ = new CMsgWriter(&cp, os);
writer_ = new CMsgWriter(&server, os);
vlog.debug("Authentication success!");
authSuccess();
writer_->writeClientInit(shared);
......@@ -306,6 +341,16 @@ void CConnection::setDesktopSize(int w, int h)
decoder.flush();
CMsgHandler::setDesktopSize(w,h);
if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());
resizeFramebuffer();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());
}
void CConnection::setExtendedDesktopSize(unsigned reason,
......@@ -316,6 +361,59 @@ void CConnection::setExtendedDesktopSize(unsigned reason,
decoder.flush();
CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());
resizeFramebuffer();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());
}
void CConnection::endOfContinuousUpdates()
{
CMsgHandler::endOfContinuousUpdates();
// We've gotten the marker for a format change, so make the pending
// one active
if (pendingPFChange) {
server.setPF(pendingPF);
pendingPFChange = false;
// We might have another change pending
if (formatChange)
requestNewUpdate();
}
}
void CConnection::serverInit(int width, int height,
const PixelFormat& pf,
const char* name)
{
CMsgHandler::serverInit(width, height, pf, name);
state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");
initDone();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());
// We want to make sure we call SetEncodings at least once
encodingChange = true;
requestNewUpdate();
// This initial update request is a bit of a corner case, so we need
// to help out setting the correct format here.
if (pendingPFChange) {
server.setPF(pendingPF);
pendingPFChange = false;
}
}
void CConnection::readAndDecodeRect(const Rect& r, int encoding,
......@@ -328,6 +426,13 @@ void CConnection::readAndDecodeRect(const Rect& r, int encoding,
void CConnection::framebufferUpdateStart()
{
CMsgHandler::framebufferUpdateStart();
assert(framebuffer != NULL);
// Note: This might not be true if continuous updates are supported
pendingUpdate = false;
requestNewUpdate();
}
void CConnection::framebufferUpdateEnd()
......@@ -335,6 +440,25 @@ void CConnection::framebufferUpdateEnd()
decoder.flush();
CMsgHandler::framebufferUpdateEnd();
// A format change has been scheduled and we are now past the update
// with the old format. Time to active the new one.
if (pendingPFChange && !continuousUpdates) {
server.setPF(pendingPF);
pendingPFChange = false;
}
if (firstUpdate) {
if (server.supportsContinuousUpdates) {
vlog.info("Enabling continuous updates");
continuousUpdates = true;
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());
}
firstUpdate = false;
}
}
void CConnection::dataRect(const Rect& r, int encoding)
......@@ -342,14 +466,190 @@ void CConnection::dataRect(const Rect& r, int encoding)
decoder.decodeRect(r, encoding, framebuffer);
}
void CConnection::serverCutText(const char* str)
{
hasLocalClipboard = false;
strFree(serverClipboard);
serverClipboard = NULL;
serverClipboard = latin1ToUTF8(str);
handleClipboardAnnounce(true);
}
void CConnection::handleClipboardCaps(rdr::U32 flags,
const rdr::U32* lengths)
{
rdr::U32 sizes[] = { 0 };
CMsgHandler::handleClipboardCaps(flags, lengths);
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
rfb::clipboardRequest |
rfb::clipboardPeek |
rfb::clipboardNotify |
rfb::clipboardProvide,
sizes);
}
void CConnection::handleClipboardRequest(rdr::U32 flags)
{
if (!(flags & rfb::clipboardUTF8))
return;
if (!hasLocalClipboard)
return;
handleClipboardRequest();
}
void CConnection::handleClipboardPeek(rdr::U32 flags)
{
if (!hasLocalClipboard)
return;
if (server.clipboardFlags() & rfb::clipboardNotify)
writer()->writeClipboardNotify(rfb::clipboardUTF8);
}
void CConnection::handleClipboardNotify(rdr::U32 flags)
{
strFree(serverClipboard);
serverClipboard = NULL;
if (flags & rfb::clipboardUTF8) {
hasLocalClipboard = false;
handleClipboardAnnounce(true);
} else {
handleClipboardAnnounce(false);
}
}
void CConnection::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
if (!(flags & rfb::clipboardUTF8))
return;
strFree(serverClipboard);
serverClipboard = NULL;
serverClipboard = convertLF((const char*)data[0], lengths[0]);
// FIXME: Should probably verify that this data was actually requested
handleClipboardData(serverClipboard);
}
void CConnection::authSuccess()
{
}
void CConnection::serverInit()
void CConnection::initDone()
{
state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");
}
void CConnection::resizeFramebuffer()
{
assert(false);
}
void CConnection::handleClipboardRequest()
{
}
void CConnection::handleClipboardAnnounce(bool available)
{
}
void CConnection::handleClipboardData(const char* data)
{
}
void CConnection::requestClipboard()
{
if (serverClipboard != NULL) {
handleClipboardData(serverClipboard);
return;
}
if (server.clipboardFlags() & rfb::clipboardRequest)
writer()->writeClipboardRequest(rfb::clipboardUTF8);
}
void CConnection::announceClipboard(bool available)
{
hasLocalClipboard = available;
if (server.clipboardFlags() & rfb::clipboardNotify)
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
else {
if (available)
handleClipboardRequest();
}
}
void CConnection::sendClipboardData(const char* data)
{
if (server.clipboardFlags() & rfb::clipboardProvide) {
CharArray filtered(convertCRLF(data));
size_t sizes[1] = { strlen(filtered.buf) + 1 };
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
} else {
CharArray latin1(utf8ToLatin1(data));
writer()->writeClientCutText(latin1.buf);
}
}
void CConnection::refreshFramebuffer()
{
forceNonincremental = true;
// Without continuous updates we have to make sure we only have a
// single update in flight, so we'll have to wait to do the refresh
if (continuousUpdates)
requestNewUpdate();
}
void CConnection::setPreferredEncoding(int encoding)
{
if (preferredEncoding == encoding)
return;
preferredEncoding = encoding;
encodingChange = true;
}
int CConnection::getPreferredEncoding()
{
return preferredEncoding;
}
void CConnection::setCompressLevel(int level)
{
if (compressLevel == level)
return;
compressLevel = level;
encodingChange = true;
}
void CConnection::setQualityLevel(int level)
{
if (qualityLevel == level)
return;
qualityLevel = level;
encodingChange = true;
}
void CConnection::setPF(const PixelFormat& pf)
{
if (server.pf().equal(pf) && !formatChange)
return;
nextPF = pf;
formatChange = true;
}
void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
......@@ -364,3 +664,98 @@ void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
writer()->writeFence(flags, len, data);
}
// requestNewUpdate() requests an update from the server, having set the
// format and encoding appropriately.
void CConnection::requestNewUpdate()
{
if (formatChange && !pendingPFChange) {
/* Catch incorrect requestNewUpdate calls */
assert(!pendingUpdate || continuousUpdates);
// We have to make sure we switch the internal format at a safe
// time. For continuous updates we temporarily disable updates and
// look for a EndOfContinuousUpdates message to see when to switch.
// For classical updates we just got a new update right before this
// function was called, so we need to make sure we finish that
// update before we can switch.
pendingPFChange = true;
pendingPF = nextPF;
if (continuousUpdates)
writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
writer()->writeSetPixelFormat(pendingPF);
if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());
formatChange = false;
}
if (encodingChange) {
updateEncodings();
encodingChange = false;
}
if (forceNonincremental || !continuousUpdates) {
pendingUpdate = true;
writer()->writeFramebufferUpdateRequest(Rect(0, 0,
server.width(),
server.height()),
!forceNonincremental);
}
forceNonincremental = false;
}
// Ask for encodings based on which decoders are supported. Assumes higher
// encoding numbers are more desirable.
void CConnection::updateEncodings()
{
std::list<rdr::U32> encodings;
if (supportsLocalCursor) {
encodings.push_back(pseudoEncodingCursorWithAlpha);
encodings.push_back(pseudoEncodingVMwareCursor);
encodings.push_back(pseudoEncodingCursor);
encodings.push_back(pseudoEncodingXCursor);
}
if (supportsDesktopResize) {
encodings.push_back(pseudoEncodingDesktopSize);
encodings.push_back(pseudoEncodingExtendedDesktopSize);
}
if (supportsLEDState) {
encodings.push_back(pseudoEncodingLEDState);
encodings.push_back(pseudoEncodingVMwareLEDState);
}
encodings.push_back(pseudoEncodingDesktopName);
encodings.push_back(pseudoEncodingLastRect);
encodings.push_back(pseudoEncodingExtendedClipboard);
encodings.push_back(pseudoEncodingContinuousUpdates);
encodings.push_back(pseudoEncodingFence);
encodings.push_back(pseudoEncodingQEMUKeyEvent);
if (Decoder::supported(preferredEncoding)) {
encodings.push_back(preferredEncoding);
}
encodings.push_back(encodingCopyRect);
for (int i = encodingMax; i >= 0; i--) {
if ((i != preferredEncoding) && Decoder::supported(i))
encodings.push_back(i);
}
if (compressLevel >= 0 && compressLevel <= 9)
encodings.push_back(pseudoEncodingCompressLevel0 + compressLevel);
if (qualityLevel >= 0 && qualityLevel <= 9)
encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel);
writer()->writeSetEncodings(encodings);
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2017 Pierre Ossman for Cendio AB
* Copyright 2011-2019 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
......@@ -62,10 +62,6 @@ namespace rfb {
// server upon initialisation.
void setShared(bool s) { shared = s; }
// setProtocol3_3 configures whether or not the CConnection should
// only ever support protocol version 3.3
void setProtocol3_3(bool s) {useProtocol3_3 = s;}
// setFramebuffer configures the PixelBuffer that the CConnection
// should render all pixel data in to. Note that the CConnection
// takes ownership of the PixelBuffer and it must not be deleted by
......@@ -100,6 +96,12 @@ namespace rfb {
int w, int h,
const ScreenSet& layout);
virtual void endOfContinuousUpdates();
virtual void serverInit(int width, int height,
const PixelFormat& pf,
const char* name);
virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb);
......@@ -107,23 +109,90 @@ namespace rfb {
virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding);
virtual void serverCutText(const char* str);
// Methods to be overridden in a derived class
virtual void handleClipboardCaps(rdr::U32 flags,
const rdr::U32* lengths);
virtual void handleClipboardRequest(rdr::U32 flags);
virtual void handleClipboardPeek(rdr::U32 flags);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
// getIdVerifier() returns the identity verifier associated with the connection.
// Ownership of the IdentityVerifier is retained by the CConnection instance.
virtual IdentityVerifier* getIdentityVerifier() {return 0;}
// Methods to be overridden in a derived class
// authSuccess() is called when authentication has succeeded.
virtual void authSuccess();
// serverInit() is called when the ServerInit message is received. The
// derived class must call on to CConnection::serverInit().
virtual void serverInit();
// initDone() is called when the connection is fully established
// and standard messages can be sent. This is called before the
// initial FramebufferUpdateRequest giving a derived class the
// chance to modify pixel format and settings. The derived class
// must also make sure it has provided a valid framebuffer before
// returning.
virtual void initDone() = 0;
// resizeFramebuffer() is called whenever the framebuffer
// dimensions or the screen layout changes. A subclass must make
// sure the pixel buffer has been updated once this call returns.
virtual void resizeFramebuffer();
// handleClipboardRequest() is called whenever the server requests
// the client to send over its clipboard data. It will only be
// called after the client has first announced a clipboard change
// via announceClipboard().
virtual void handleClipboardRequest();
// handleClipboardAnnounce() is called to indicate a change in the
// clipboard on the server. Call requestClipboard() to access the
// actual data.
virtual void handleClipboardAnnounce(bool available);
// handleClipboardData() is called when the server has sent over
// the clipboard data as a result of a previous call to
// requestClipboard(). Note that this function might never be
// called if the clipboard data was no longer available when the
// server received the request.
virtual void handleClipboardData(const char* data);
// Other methods
// requestClipboard() will result in a request to the server to
// transfer its clipboard data. A call to handleClipboardData()
// will be made once the data is available.
virtual void requestClipboard();
// announceClipboard() informs the server of changes to the
// clipboard on the client. The server may later request the
// clipboard data via handleClipboardRequest().
virtual void announceClipboard(bool available);
// sendClipboardData() transfers the clipboard data to the server
// and should be called whenever the server has requested the
// clipboard via handleClipboardRequest().
virtual void sendClipboardData(const char* data);
// refreshFramebuffer() forces a complete refresh of the entire
// framebuffer
void refreshFramebuffer();
// setPreferredEncoding()/getPreferredEncoding() adjusts which
// encoding is listed first as a hint to the server that it is the
// preferred one
void setPreferredEncoding(int encoding);
int getPreferredEncoding();
// setCompressLevel()/setQualityLevel() controls the encoding hints
// sent to the server
void setCompressLevel(int level);
void setQualityLevel(int level);
// setPF() controls the pixel format requested from the server.
// server.pf() will automatically be adjusted once the new format
// is active.
void setPF(const PixelFormat& pf);
CMsgReader* reader() { return reader_; }
CMsgWriter* writer() { return writer_; }
......@@ -159,6 +228,13 @@ namespace rfb {
ModifiablePixelBuffer* getFramebuffer() { return framebuffer; }
protected:
// Optional capabilities that a subclass is expected to set to true
// if supported
bool supportsLocalCursor;
bool supportsDesktopResize;
bool supportsLEDState;
private:
// This is a default implementation of fences that automatically
// responds to requests, stating no support for synchronisation.
......@@ -176,6 +252,9 @@ namespace rfb {
void throwConnFailedException();
void securityCompleted();
void requestNewUpdate();
void updateEncodings();
rdr::InStream* is;
rdr::OutStream* os;
CMsgReader* reader_;
......@@ -186,10 +265,28 @@ namespace rfb {
CharArray serverName;
bool useProtocol3_3;
bool pendingPFChange;
rfb::PixelFormat pendingPF;
int preferredEncoding;
int compressLevel;
int qualityLevel;
bool formatChange;
rfb::PixelFormat nextPF;
bool encodingChange;
bool firstUpdate;
bool pendingUpdate;
bool continuousUpdates;
bool forceNonincremental;
ModifiablePixelBuffer* framebuffer;
DecodeManager decoder;
char* serverClipboard;
bool hasLocalClipboard;
};
}
#endif
......@@ -11,9 +11,9 @@ set(RFB_SOURCES
CSecurityStack.cxx
CSecurityVeNCrypt.cxx
CSecurityVncAuth.cxx
ClientParams.cxx
ComparingUpdateTracker.cxx
Configuration.cxx
ConnParams.cxx
CopyRectDecoder.cxx
Cursor.cxx
DecodeManager.cxx
......@@ -21,7 +21,6 @@ set(RFB_SOURCES
d3des.c
EncodeManager.cxx
Encoder.cxx
HTTPServer.cxx
HextileDecoder.cxx
HextileEncoder.cxx
JpegCompressor.cxx
......@@ -44,6 +43,7 @@ set(RFB_SOURCES
SMsgReader.cxx
SMsgWriter.cxx
ServerCore.cxx
ServerParams.cxx
Security.cxx
SecurityServer.cxx
SecurityClient.cxx
......
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 Pierre Ossman for Cendio AB
* Copyright 2009-2019 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
......@@ -19,9 +19,12 @@
#include <stdio.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/CMsgHandler.h>
#include <rfb/screenTypes.h>
static rfb::LogWriter vlog("CMsgHandler");
using namespace rfb;
CMsgHandler::CMsgHandler()
......@@ -34,50 +37,53 @@ CMsgHandler::~CMsgHandler()
void CMsgHandler::setDesktopSize(int width, int height)
{
cp.width = width;
cp.height = height;
server.setDimensions(width, height);
}
void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result,
int width, int height,
const ScreenSet& layout)
{
cp.supportsSetDesktopSize = true;
server.supportsSetDesktopSize = true;
if ((reason == reasonClient) && (result != resultSuccess))
return;
if (!layout.validate(width, height))
fprintf(stderr, "Server sent us an invalid screen layout\n");
cp.width = width;
cp.height = height;
cp.screenLayout = layout;
server.setDimensions(width, height, layout);
}
void CMsgHandler::setPixelFormat(const PixelFormat& pf)
{
cp.setPF(pf);
server.setPF(pf);
}
void CMsgHandler::setName(const char* name)
{
cp.setName(name);
server.setName(name);
}
void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[])
{
cp.supportsFence = true;
server.supportsFence = true;
}
void CMsgHandler::endOfContinuousUpdates()
{
cp.supportsContinuousUpdates = true;
server.supportsContinuousUpdates = true;
}
void CMsgHandler::supportsQEMUKeyEvent()
{
cp.supportsQEMUKeyEvent = true;
server.supportsQEMUKeyEvent = true;
}
void CMsgHandler::serverInit(int width, int height,
const PixelFormat& pf,
const char* name)
{
server.setDimensions(width, height);
server.setPF(pf);
server.setName(name);
}
void CMsgHandler::framebufferUpdateStart()
......@@ -90,5 +96,28 @@ void CMsgHandler::framebufferUpdateEnd()
void CMsgHandler::setLEDState(unsigned int state)
{
cp.setLEDState(state);
server.setLEDState(state);
}
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
{
server.setClipboardCaps(flags, lengths);
}
void CMsgHandler::handleClipboardRequest(rdr::U32 flags)
{
}
void CMsgHandler::handleClipboardPeek(rdr::U32 flags)
{
}
void CMsgHandler::handleClipboardNotify(rdr::U32 flags)
{
}
void CMsgHandler::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 Pierre Ossman for Cendio AB
* Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
......@@ -26,7 +26,7 @@
#include <rdr/types.h>
#include <rfb/Pixel.h>
#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/Rect.h>
#include <rfb/ScreenSet.h>
......@@ -41,9 +41,10 @@ namespace rfb {
// The following methods are called as corresponding messages are read. A
// derived class should override these methods as desired. Note that for
// the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat() and
// setName() methods, a derived class should call on to CMsgHandler's
// methods to set the members of cp appropriately.
// the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat(),
// setName(), serverInit() and clipboardCaps methods, a derived class
// should call on to CMsgHandler's methods to set the members of "server"
// appropriately.
virtual void setDesktopSize(int w, int h);
virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
......@@ -56,7 +57,9 @@ namespace rfb {
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void endOfContinuousUpdates();
virtual void supportsQEMUKeyEvent();
virtual void serverInit() = 0;
virtual void serverInit(int width, int height,
const PixelFormat& pf,
const char* name) = 0;
virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) = 0;
......@@ -68,11 +71,20 @@ namespace rfb {
virtual void setColourMapEntries(int firstColour, int nColours,
rdr::U16* rgbs) = 0;
virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0;
virtual void serverCutText(const char* str) = 0;
virtual void setLEDState(unsigned int state);
ConnParams cp;
virtual void handleClipboardCaps(rdr::U32 flags,
const rdr::U32* lengths);
virtual void handleClipboardRequest(rdr::U32 flags);
virtual void handleClipboardPeek(rdr::U32 flags);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
ServerParams server;
};
}
#endif
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2017 Pierre Ossman for Cendio AB
* Copyright 2009-2019 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
......@@ -20,13 +20,21 @@
#include <assert.h>
#include <stdio.h>
#include <rfb/msgTypes.h>
#include <rdr/InStream.h>
#include <rdr/ZlibInStream.h>
#include <rfb/msgTypes.h>
#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
#include <rfb/CMsgHandler.h>
#include <rfb/CMsgReader.h>
static rfb::LogWriter vlog("CMsgReader");
static rfb::IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
using namespace rfb;
CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
......@@ -43,13 +51,10 @@ void CMsgReader::readServerInit()
{
int width = is->readU16();
int height = is->readU16();
handler->setDesktopSize(width, height);
PixelFormat pf;
pf.read(is);
handler->setPixelFormat(pf);
CharArray name(is->readString());
handler->setName(name.buf);
handler->serverInit();
handler->serverInit(width, height, pf, name.buf);
}
void CMsgReader::readMsg()
......@@ -77,7 +82,7 @@ void CMsgReader::readMsg()
readEndOfContinuousUpdates();
break;
default:
fprintf(stderr, "unknown message type %d\n", type);
vlog.error("unknown message type %d", type);
throw Exception("unknown message type");
}
} else {
......@@ -100,6 +105,9 @@ void CMsgReader::readMsg()
case pseudoEncodingCursorWithAlpha:
readSetCursorWithAlpha(w, h, Point(x,y));
break;
case pseudoEncodingVMwareCursor:
readSetVMwareCursor(w, h, Point(x,y));
break;
case pseudoEncodingDesktopName:
readSetDesktopName(x, y, w, h);
break;
......@@ -111,6 +119,10 @@ void CMsgReader::readMsg()
break;
case pseudoEncodingLEDState:
readLEDState();
break;
case pseudoEncodingVMwareLEDState:
readVMwareLEDState();
break;
case pseudoEncodingQEMUKeyEvent:
handler->supportsQEMUKeyEvent();
break;
......@@ -145,15 +157,116 @@ void CMsgReader::readServerCutText()
{
is->skip(3);
rdr::U32 len = is->readU32();
if (len > 256*1024) {
if (len & 0x80000000) {
rdr::S32 slen = len;
slen = -slen;
readExtendedClipboard(slen);
return;
}
if (len > (size_t)maxCutText) {
is->skip(len);
fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
vlog.error("cut text too long (%d bytes) - ignoring",len);
return;
}
CharArray ca(len+1);
ca.buf[len] = 0;
CharArray ca(len);
is->readBytes(ca.buf, len);
handler->serverCutText(ca.buf, len);
CharArray filtered(convertLF(ca.buf, len));
handler->serverCutText(filtered.buf);
}
void CMsgReader::readExtendedClipboard(rdr::S32 len)
{
rdr::U32 flags;
rdr::U32 action;
if (len < 4)
throw Exception("Invalid extended clipboard message");
if (len > maxCutText) {
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
is->skip(len);
return;
}
flags = is->readU32();
action = flags & clipboardActionMask;
if (action & clipboardCaps) {
int i;
size_t num;
rdr::U32 lengths[16];
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
num++;
}
if (len < (rdr::S32)(4 + 4*num))
throw Exception("Invalid extended clipboard message");
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
lengths[num++] = is->readU32();
}
handler->handleClipboardCaps(flags, lengths);
} else if (action == clipboardProvide) {
rdr::ZlibInStream zis;
int i;
size_t num;
size_t lengths[16];
rdr::U8* buffers[16];
zis.setUnderlying(is, len - 4);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
lengths[num] = zis.readU32();
if (lengths[num] > (size_t)maxCutText) {
vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
(unsigned)lengths[num]);
zis.skip(lengths[num]);
flags &= ~(1 << i);
continue;
}
buffers[num] = new rdr::U8[lengths[num]];
zis.readBytes(buffers[num], lengths[num]);
num++;
}
zis.removeUnderlying();
handler->handleClipboardProvide(flags, lengths, buffers);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
delete [] buffers[num++];
}
} else {
switch (action) {
case clipboardRequest:
handler->handleClipboardRequest(flags);
break;
case clipboardPeek:
handler->handleClipboardPeek(flags);
break;
case clipboardNotify:
handler->handleClipboardNotify(flags);
break;
default:
throw Exception("Invalid extended clipboard action");
}
}
}
void CMsgReader::readFence()
......@@ -168,7 +281,7 @@ void CMsgReader::readFence()
len = is->readU8();
if (len > sizeof(data)) {
fprintf(stderr, "Ignoring fence with too large payload\n");
vlog.error("Ignoring fence with too large payload");
is->skip(len);
return;
}
......@@ -192,15 +305,16 @@ void CMsgReader::readFramebufferUpdate()
void CMsgReader::readRect(const Rect& r, int encoding)
{
if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) {
fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
if ((r.br.x > handler->server.width()) ||
(r.br.y > handler->server.height())) {
vlog.error("Rect too big: %dx%d at %d,%d exceeds %dx%d",
r.width(), r.height(), r.tl.x, r.tl.y,
handler->cp.width, handler->cp.height);
handler->server.width(), handler->server.height());
throw Exception("Rect too big");
}
if (r.is_empty())
fprintf(stderr, "Warning: zero size rect\n");
vlog.error("zero size rect");
handler->dataRect(r, encoding);
}
......@@ -210,18 +324,19 @@ void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor");
rdr::U8 pr, pg, pb;
rdr::U8 sr, sg, sb;
int data_len = ((width+7)/8) * height;
int mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
int x, y;
rdr::U8 buf[width*height*4];
rdr::U8* out;
rdr::U8Array rgba(width*height*4);
if (width * height > 0) {
rdr::U8 pr, pg, pb;
rdr::U8 sr, sg, sb;
int data_len = ((width+7)/8) * height;
int mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
int x, y;
rdr::U8* out;
pr = is->readU8();
pg = is->readU8();
pb = is->readU8();
......@@ -232,35 +347,35 @@ void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
is->readBytes(data.buf, data_len);
is->readBytes(mask.buf, mask_len);
}
int maskBytesPerRow = (width+7)/8;
out = buf;
for (y = 0;y < height;y++) {
for (x = 0;x < width;x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
if (data.buf[byte] & (1 << bit)) {
out[0] = pr;
out[1] = pg;
out[2] = pb;
} else {
out[0] = sr;
out[1] = sg;
out[2] = sb;
int maskBytesPerRow = (width+7)/8;
out = rgba.buf;
for (y = 0;y < height;y++) {
for (x = 0;x < width;x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
if (data.buf[byte] & (1 << bit)) {
out[0] = pr;
out[1] = pg;
out[2] = pb;
} else {
out[0] = sr;
out[1] = sg;
out[2] = sb;
}
if (mask.buf[byte] & (1 << bit))
out[3] = 255;
else
out[3] = 0;
out += 4;
}
if (mask.buf[byte] & (1 << bit))
out[3] = 255;
else
out[3] = 0;
out += 4;
}
}
handler->setCursor(width, height, hotspot, buf);
handler->setCursor(width, height, hotspot, rgba.buf);
}
void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
......@@ -268,13 +383,13 @@ void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor");
int data_len = width * height * (handler->cp.pf().bpp/8);
int data_len = width * height * (handler->server.pf().bpp/8);
int mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
int x, y;
rdr::U8 buf[width*height*4];
rdr::U8Array rgba(width*height*4);
rdr::U8* in;
rdr::U8* out;
......@@ -283,25 +398,25 @@ void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
int maskBytesPerRow = (width+7)/8;
in = data.buf;
out = buf;
out = rgba.buf;
for (y = 0;y < height;y++) {
for (x = 0;x < width;x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
handler->cp.pf().rgbFromBuffer(out, in, 1);
handler->server.pf().rgbFromBuffer(out, in, 1);
if (mask.buf[byte] & (1 << bit))
out[3] = 255;
else
out[3] = 0;
in += handler->cp.pf().bpp/8;
in += handler->server.pf().bpp/8;
out += 4;
}
}
handler->setCursor(width, height, hotspot, buf);
handler->setCursor(width, height, hotspot, rgba.buf);
}
void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
......@@ -320,10 +435,10 @@ void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hots
encoding = is->readS32();
origPF = handler->cp.pf();
handler->cp.setPF(rgbaPF);
origPF = handler->server.pf();
handler->server.setPF(rgbaPF);
handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
handler->cp.setPF(origPF);
handler->server.setPF(origPF);
// On-wire data has pre-multiplied alpha, but we store it
// non-pre-multiplied
......@@ -350,12 +465,100 @@ void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hots
pb.getBuffer(pb.getRect(), &stride));
}
void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
{
if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor");
rdr::U8 type;
type = is->readU8();
is->skip(1);
if (type == 0) {
int len = width * height * (handler->server.pf().bpp/8);
rdr::U8Array andMask(len);
rdr::U8Array xorMask(len);
rdr::U8Array data(width*height*4);
rdr::U8* andIn;
rdr::U8* xorIn;
rdr::U8* out;
int Bpp;
is->readBytes(andMask.buf, len);
is->readBytes(xorMask.buf, len);
andIn = andMask.buf;
xorIn = xorMask.buf;
out = data.buf;
Bpp = handler->server.pf().bpp/8;
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
Pixel andPixel, xorPixel;
andPixel = handler->server.pf().pixelFromBuffer(andIn);
xorPixel = handler->server.pf().pixelFromBuffer(xorIn);
andIn += Bpp;
xorIn += Bpp;
if (andPixel == 0) {
rdr::U8 r, g, b;
// Opaque pixel
handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b);
*out++ = r;
*out++ = g;
*out++ = b;
*out++ = 0xff;
} else if (xorPixel == 0) {
// Fully transparent pixel
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = 0;
} else if (andPixel == xorPixel) {
// Inverted pixel
// We don't really support this, so just turn the pixel black
// FIXME: Do an outline like WinVNC does?
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = 0xff;
} else {
// Partially transparent/inverted pixel
// We _really_ can't handle this, just make it black
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = 0xff;
}
}
}
handler->setCursor(width, height, hotspot, data.buf);
} else if (type == 1) {
rdr::U8Array data(width*height*4);
// FIXME: Is alpha premultiplied?
is->readBytes(data.buf, width*height*4);
handler->setCursor(width, height, hotspot, data.buf);
} else {
throw Exception("Unknown cursor type");
}
}
void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
{
char* name = is->readString();
if (x || y || w || h) {
fprintf(stderr, "Ignoring DesktopName rect with non-zero position/size\n");
vlog.error("Ignoring DesktopName rect with non-zero position/size");
} else {
handler->setName(name);
}
......@@ -395,3 +598,15 @@ void CMsgReader::readLEDState()
handler->setLEDState(state);
}
void CMsgReader::readVMwareLEDState()
{
rdr::U32 state;
state = is->readU32();
// As luck has it, this extension uses the same bit definitions,
// so no conversion required
handler->setLEDState(state);
}