From dc21d5f97cf7be331aa036048c9bb1438528dd68 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 9 Sep 2021 12:46:57 -0400 Subject: [PATCH 01/22] Add a set of self-microbenchmarks (#51) Co-authored-by: Lauri Kasanen --- common/network/GetAPIMessager.cxx | 5 +- common/rfb/CMakeLists.txt | 1 + common/rfb/EncodeManager.cxx | 6 +- common/rfb/EncodeManager.h | 7 ++ common/rfb/SelfBench.cxx | 197 ++++++++++++++++++++++++++++++ common/rfb/ServerCore.cxx | 4 + common/rfb/ServerCore.h | 1 + common/rfb/VNCServerST.cxx | 5 + unix/xserver/hw/vnc/Xvnc.man | 4 + 9 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 common/rfb/SelfBench.cxx diff --git a/common/network/GetAPIMessager.cxx b/common/network/GetAPIMessager.cxx index 8ecfb67..d7abccd 100644 --- a/common/network/GetAPIMessager.cxx +++ b/common/network/GetAPIMessager.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -32,10 +33,6 @@ using namespace rfb; static LogWriter vlog("GetAPIMessager"); -PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb, - const uint16_t tgtw, const uint16_t tgth, - const float tgtdiff); - struct TightJPEGConfiguration { int quality; int subsampling; diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 38d3472..1b37dd5 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -48,6 +48,7 @@ set(RFB_SOURCES Security.cxx SecurityServer.cxx SecurityClient.cxx + SelfBench.cxx SSecurityPlain.cxx SSecurityStack.cxx SSecurityVncAuth.cxx diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index db3add5..565ab16 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -895,7 +895,7 @@ void EncodeManager::updateVideoStats(const std::vector &rects, const Pixel } } -static PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, +PixelBuffer *rfb::nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, const float diff) { ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h); @@ -920,7 +920,7 @@ static PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const return newpb; } -static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, +PixelBuffer *rfb::bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, const float diff) { ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h); @@ -968,7 +968,7 @@ static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const return newpb; } -PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb, +PixelBuffer *rfb::progressiveBilinearScale(const PixelBuffer *pb, const uint16_t tgtw, const uint16_t tgth, const float tgtdiff) { diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h index 60f7d21..bf47dbf 100644 --- a/common/rfb/EncodeManager.h +++ b/common/rfb/EncodeManager.h @@ -215,6 +215,13 @@ namespace rfb { virtual rdr::U8* getBufferRW(const Rect& r, int* stride); }; }; + + PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, + const float diff); + PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, + const float diff); + PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h, + const float diff); } #endif diff --git a/common/rfb/SelfBench.cxx b/common/rfb/SelfBench.cxx new file mode 100644 index 0000000..fd224d3 --- /dev/null +++ b/common/rfb/SelfBench.cxx @@ -0,0 +1,197 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +static LogWriter vlog("SelfBench"); + +static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16); + +#define RUNS 64 + +#define W 1600 +#define H 1200 + +void SelfBench() { + + unsigned i, runs; + struct timeval start; + + ManagedPixelBuffer f1(pfRGBX, W, H); + ManagedPixelBuffer f2(pfRGBX, W, H); + ManagedPixelBuffer screen(pfRGBX, W, H); + + int stride; + rdr::U8 *f1ptr = f1.getBufferRW(f1.getRect(), &stride); + rdr::U8 *f2ptr = f2.getBufferRW(f2.getRect(), &stride); + rdr::U8 * const screenptr = screen.getBufferRW(screen.getRect(), &stride); + + rdr::U8 * const f1orig = f1ptr; + rdr::U8 * const f2orig = f2ptr; + + for (i = 0; i < W * H * 4; i += 4) { + f1ptr[0] = rand(); + f1ptr[1] = rand(); + f1ptr[2] = rand(); + + f2ptr[0] = rand(); + f2ptr[1] = rand(); + f2ptr[2] = rand(); + + f1ptr += 4; + f2ptr += 4; + } + + vlog.info("Running micro-benchmarks (single-threaded, runs depending on task)"); + + // Encoding + std::vector vec; + + TightJPEGEncoder jpeg(NULL); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + jpeg.compressOnly(&f1, 8, vec, false); + } + vlog.info("Jpeg compression at quality 8 took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + jpeg.compressOnly(&f1, 4, vec, false); + } + vlog.info("Jpeg compression at quality 4 took %u ms (%u runs)", msSince(&start), runs); + + + TightWEBPEncoder webp(NULL); + + gettimeofday(&start, NULL); + runs = RUNS / 8; + for (i = 0; i < runs; i++) { + webp.compressOnly(&f1, 8, vec, false); + } + vlog.info("Webp compression at quality 8 took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS / 4; + for (i = 0; i < runs; i++) { + webp.compressOnly(&f1, 4, vec, false); + } + vlog.info("Webp compression at quality 4 took %u ms (%u runs)", msSince(&start), runs); + + // Scaling + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = nearestScale(&f1, W * 0.8, H * 0.8, 0.8); + delete pb; + } + vlog.info("Nearest scaling to 80%% took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = nearestScale(&f1, W * 0.4, H * 0.4, 0.4); + delete pb; + } + vlog.info("Nearest scaling to 40%% took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = bilinearScale(&f1, W * 0.8, H * 0.8, 0.8); + delete pb; + } + vlog.info("Bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = bilinearScale(&f1, W * 0.4, H * 0.4, 0.4); + delete pb; + } + vlog.info("Bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.8, H * 0.8, 0.8); + delete pb; + } + vlog.info("Progressive bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.4, H * 0.4, 0.4); + delete pb; + } + vlog.info("Progressive bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs); + + // Analysis + ComparingUpdateTracker *comparer = new ComparingUpdateTracker(&screen); + Region cursorReg; + + Server::detectScrolling.setParam(false); + Server::detectHorizontal.setParam(false); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4); + comparer->compare(true, cursorReg); + } + vlog.info("Analysis took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs); + + Server::detectScrolling.setParam(true); + + gettimeofday(&start, NULL); + runs = RUNS; + for (i = 0; i < runs; i++) { + memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4); + comparer->compare(false, cursorReg); + } + vlog.info("Analysis w/ scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs); + + Server::detectHorizontal.setParam(true); + delete comparer; + comparer = new ComparingUpdateTracker(&screen); + + gettimeofday(&start, NULL); + runs = RUNS / 2; + for (i = 0; i < runs; i++) { + memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4); + comparer->compare(false, cursorReg); + } + vlog.info("Analysis w/ horizontal scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs); + + exit(0); +} diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 3b36af0..28ce777 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -113,6 +113,10 @@ rfb::BoolParameter rfb::Server::ignoreClientSettingsKasm ("IgnoreClientSettingsKasm", "Ignore the additional client settings exposed in Kasm.", false); +rfb::BoolParameter rfb::Server::selfBench +("SelfBench", + "Run self-benchmarks and exit.", + false); rfb::IntParameter rfb::Server::dynamicQualityMin ("DynamicQualityMin", "The minimum dynamic JPEG quality, 0 = low, 9 = high", diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index 280596f..766eda0 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -74,6 +74,7 @@ namespace rfb { static BoolParameter detectScrolling; static BoolParameter detectHorizontal; static BoolParameter ignoreClientSettingsKasm; + static BoolParameter selfBench; static PresetParameter preferBandwidth; }; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index a564021..785ab42 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -76,6 +76,8 @@ static LogWriter slog("VNCServerST"); LogWriter VNCServerST::connectionsLog("Connections"); EncCache VNCServerST::encCache; +void SelfBench(); + // // -=- VNCServerST Implementation // @@ -212,6 +214,9 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) } trackingClient[0] = 0; + + if (Server::selfBench) + SelfBench(); } VNCServerST::~VNCServerST() diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index 809fc1c..f3a6ac6 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -317,6 +317,10 @@ Log clipboard and keyboard actions. Info logs just clipboard direction and size, verbose adds the contents for both. . .TP +.B \-selfBench +Run a set of self-benchmarks and exit. +. +.TP .B \-noWebsocket Disable websockets and expose a traditional VNC port (5901, etc.). . From 0cb2c0ba9f86c45a451e93548c99a45f1cd4b5c8 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 9 Sep 2021 13:55:33 -0400 Subject: [PATCH 02/22] Sse scaling (#52) * Add CPUID functions for runtime dispatch * Add SSE2 scaling --- CMakeLists.txt | 4 + common/rfb/CMakeLists.txt | 22 +++ common/rfb/EncodeManager.cxx | 60 ++++++++ common/rfb/VNCServerST.cxx | 4 + common/rfb/cpuid.cxx | 70 ++++++++++ common/rfb/cpuid.h | 28 ++++ common/rfb/scale_dummy.cxx | 37 +++++ common/rfb/scale_sse2.cxx | 257 +++++++++++++++++++++++++++++++++++ common/rfb/scale_sse2.h | 38 ++++++ kasmweb | 2 +- 10 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 common/rfb/cpuid.cxx create mode 100644 common/rfb/cpuid.h create mode 100644 common/rfb/scale_dummy.cxx create mode 100644 common/rfb/scale_sse2.cxx create mode 100644 common/rfb/scale_sse2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 179c278..c32884a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ include(CheckLibraryExists) include(CheckTypeSize) include(CheckCSourceCompiles) include(CheckCXXSourceCompiles) +include(CheckCXXCompilerFlag) include(CheckCSourceRuns) include(CMakeMacroLibtoolFile) @@ -208,6 +209,9 @@ if(ENABLE_PAM) endif() set(HAVE_PAM ${ENABLE_PAM}) +# Check for SSE2 +check_cxx_compiler_flag(-msse2 COMPILER_SUPPORTS_SSE2) + # Generate config.h and make sure the source finds it configure_file(config.h.in config.h) add_definitions(-DHAVE_CONFIG_H) diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 1b37dd5..dea5f35 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -64,6 +64,7 @@ set(RFB_SOURCES VNCServerST.cxx ZRLEEncoder.cxx ZRLEDecoder.cxx + cpuid.cxx encodings.cxx util.cxx xxhash.c) @@ -97,6 +98,27 @@ if(GNUTLS_FOUND) ) endif() +# SSE2 + +set(SSE2_SOURCES + scale_sse2.cxx) + +set(SCALE_DUMMY_SOURCES + scale_dummy.cxx) + +if(COMPILER_SUPPORTS_SSE2) + set_source_files_properties(${SSE2_SOURCES} PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -msse2) + set(RFB_SOURCES + ${RFB_SOURCES} + ${SSE2_SOURCES} + ) +else() + set(RFB_SOURCES + ${RFB_SOURCES} + ${SCALE_DUMMY_SOURCES} + ) +endif() + add_library(rfb STATIC ${RFB_SOURCES}) target_link_libraries(rfb ${RFB_LIBRARIES}) diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index 565ab16..30489c7 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -22,10 +22,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -972,6 +974,64 @@ PixelBuffer *rfb::progressiveBilinearScale(const PixelBuffer *pb, const uint16_t tgtw, const uint16_t tgth, const float tgtdiff) { + if (supportsSSE2()) { + if (tgtdiff >= 0.5f) { + ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth); + + int oldstride, newstride; + const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride); + rdr::U8 *newpx = newpb->getBufferRW(newpb->getRect(), &newstride); + + SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff); + return newpb; + } + + PixelBuffer *newpb; + uint16_t neww, newh, oldw, oldh; + bool del = false; + + do { + oldw = pb->getRect().width(); + oldh = pb->getRect().height(); + neww = oldw / 2; + newh = oldh / 2; + + newpb = new ManagedPixelBuffer(pb->getPF(), neww, newh); + + int oldstride, newstride; + const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride); + rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(), + &newstride); + + SSE2_halve(oldpx, neww, newh, newpx, oldstride, newstride); + + if (del) + delete pb; + del = true; + + pb = newpb; + } while (tgtw * 2 < neww); + + // Final, non-halving step + if (tgtw != neww || tgth != newh) { + oldw = pb->getRect().width(); + oldh = pb->getRect().height(); + + newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth); + + int oldstride, newstride; + const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride); + rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(), + &newstride); + + SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff); + if (del) + delete pb; + } + + return newpb; + } // SSE2 + if (tgtdiff >= 0.5f) return bilinearScale(pb, tgtw, tgth, tgtdiff); diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 785ab42..3e87892 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -53,6 +53,7 @@ #include +#include #include #include #include @@ -134,6 +135,9 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) { lastUserInputTime = lastDisconnectTime = time(0); slog.debug("creating single-threaded server %s", name.buf); + slog.info("CPU capability: SSE2 %s, AVX512f %s", + supportsSSE2() ? "yes" : "no", + supportsAVX512f() ? "yes" : "no"); DLPRegion.enabled = DLPRegion.percents = false; diff --git a/common/rfb/cpuid.cxx b/common/rfb/cpuid.cxx new file mode 100644 index 0000000..c89f950 --- /dev/null +++ b/common/rfb/cpuid.cxx @@ -0,0 +1,70 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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. + */ + +#include + +static uint32_t cpuid[4] = { 0 }; +static uint32_t extcpuid[4] = { 0 }; + +static void getcpuid() { + if (cpuid[0]) + return; + +#if defined(__x86_64__) || defined(__i386__) + uint32_t eax, ecx = 0; + + eax = 1; // normal feature bits + + __asm__ __volatile__( + "cpuid\n\t" + : "=a"(cpuid[0]), "=b"(cpuid[1]), "=c"(cpuid[2]), "=d"(cpuid[3]) + : "0"(eax), "2"(ecx) + ); + + eax = 7; // ext feature bits + ecx = 0; + + __asm__ __volatile__( + "cpuid\n\t" + : "=a"(extcpuid[0]), "=b"(extcpuid[1]), "=c"(extcpuid[2]), "=d"(extcpuid[3]) + : "0"(eax), "2"(ecx) + ); +#endif +} + +namespace rfb { + +bool supportsSSE2() { + getcpuid(); +#if defined(__x86_64__) || defined(__i386__) + #define bit_SSE2 (1 << 26) + return cpuid[3] & bit_SSE2; +#endif + return false; +} + +bool supportsAVX512f() { + getcpuid(); +#if defined(__x86_64__) || defined(__i386__) + #define bit_AVX512f (1 << 16) + return extcpuid[1] & bit_AVX512f; +#endif + return false; +} + +}; // namespace rfb diff --git a/common/rfb/cpuid.h b/common/rfb/cpuid.h new file mode 100644 index 0000000..c84b4a2 --- /dev/null +++ b/common/rfb/cpuid.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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 __RFB_CPUID_H__ +#define __RFB_CPUID_H__ + +namespace rfb { + + bool supportsSSE2(); + bool supportsAVX512f(); +}; + +#endif diff --git a/common/rfb/scale_dummy.cxx b/common/rfb/scale_dummy.cxx new file mode 100644 index 0000000..b978609 --- /dev/null +++ b/common/rfb/scale_dummy.cxx @@ -0,0 +1,37 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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. + */ + +#include + +namespace rfb { + +void SSE2_halve(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride) { +} + +// Handles factors between 0.5 and 1.0 +void SSE2_scale(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride, + const float tgtdiff) { +} + +}; // namespace rfb diff --git a/common/rfb/scale_sse2.cxx b/common/rfb/scale_sse2.cxx new file mode 100644 index 0000000..e4c717b --- /dev/null +++ b/common/rfb/scale_sse2.cxx @@ -0,0 +1,257 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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. + */ + +#include + +#include + +namespace rfb { + +/* +static void print128(const char msg[], const __m128i v) { + union { + __m128i v; + uint8_t c[16]; + } u; + + u.v = v; + + printf("%s %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n", + msg, + u.c[0], + u.c[1], + u.c[2], + u.c[3], + u.c[4], + u.c[5], + u.c[6], + u.c[7], + u.c[8], + u.c[9], + u.c[10], + u.c[11], + u.c[12], + u.c[13], + u.c[14], + u.c[15]); +} +*/ + +void SSE2_halve(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride) { + uint16_t x, y; + const uint16_t srcw = tgtw * 2, srch = tgth * 2; + const __m128i zero = _mm_setzero_si128(); + const __m128i shift = _mm_set_epi32(0, 0, 0, 2); + const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff); + const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0); + + for (y = 0; y < srch; y += 2) { + const uint8_t * const row0 = oldpx + oldstride * y * 4; + const uint8_t * const row1 = oldpx + oldstride * (y + 1) * 4; + + uint8_t * const dst = newpx + newstride * (y / 2) * 4; + + for (x = 0; x < srcw; x += 4) { + __m128i lo, hi, a, b, c, d; + lo = _mm_loadu_si128((__m128i *) &row0[x * 4]); + hi = _mm_loadu_si128((__m128i *) &row1[x * 4]); + + a = _mm_unpacklo_epi8(lo, zero); + b = _mm_unpackhi_epi8(lo, zero); + c = _mm_unpacklo_epi8(hi, zero); + d = _mm_unpackhi_epi8(hi, zero); + + a = _mm_add_epi16(a, c); + b = _mm_add_epi16(b, d); + + c = _mm_srli_si128(a, 8); + a = _mm_and_si128(a, low); + a = _mm_add_epi16(a, c); + + d = _mm_slli_si128(b, 8); + b = _mm_and_si128(b, high); + b = _mm_add_epi16(b, d); + + a = _mm_add_epi16(a, b); + + a = _mm_srl_epi16(a, shift); + a = _mm_packus_epi16(a, zero); + + _mm_storel_epi64((__m128i *) &dst[(x / 2) * 4], a); + } + + for (; x < srcw; x += 2) { + // Remainder in C + uint8_t i; + for (i = 0; i < 4; i++) { + dst[(x / 2) * 4 + i] = + (row0[x * 4 + i] + + row0[(x + 1) * 4 + i] + + row1[x * 4 + i] + + row1[(x + 1) * 4 + i]) / 4; + } + } + } +} + +// Handles factors between 0.5 and 1.0 +void SSE2_scale(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride, + const float tgtdiff) { + + uint16_t x, y; + const __m128i zero = _mm_setzero_si128(); + const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff); + const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0); + const float invdiff = 1 / tgtdiff; + + for (y = 0; y < tgth; y++) { + const float ny = y * invdiff; + const uint16_t lowy = ny; + const uint16_t highy = lowy + 1; + const uint16_t bot = (ny - lowy) * 256; + const uint16_t top = 256 - bot; + const uint32_t * const row0 = (uint32_t *) (oldpx + oldstride * lowy * 4); + const uint32_t * const row1 = (uint32_t *) (oldpx + oldstride * highy * 4); + const uint8_t * const brow0 = (uint8_t *) row0; + const uint8_t * const brow1 = (uint8_t *) row1; + + uint8_t * const dst = newpx + newstride * y * 4; + + const __m128i vertmul = _mm_set1_epi16(top); + const __m128i vertmul2 = _mm_set1_epi16(bot); + + for (x = 0; x < tgtw; x += 2) { + const float nx[2] = { + x * invdiff, + (x + 1) * invdiff, + }; + const uint16_t lowx[2] = { + (uint16_t) nx[0], + (uint16_t) nx[1], + }; + const uint16_t highx[2] = { + (uint16_t) (lowx[0] + 1), + (uint16_t) (lowx[1] + 1), + }; + const uint16_t right[2] = { + (uint16_t) ((nx[0] - lowx[0]) * 256), + (uint16_t) ((nx[1] - lowx[1]) * 256), + }; + const uint16_t left[2] = { + (uint16_t) (256 - right[0]), + (uint16_t) (256 - right[1]), + }; + + const __m128i horzmul = _mm_set_epi16( + right[0], + right[0], + right[0], + right[0], + left[0], + left[0], + left[0], + left[0] + ); + const __m128i horzmul2 = _mm_set_epi16( + right[1], + right[1], + right[1], + right[1], + left[1], + left[1], + left[1], + left[1] + ); + + __m128i lo, hi, a, b, c, d; + lo = _mm_setr_epi32(row0[lowx[0]], + row0[highx[0]], + row0[lowx[1]], + row0[highx[1]]); + hi = _mm_setr_epi32(row1[lowx[0]], + row1[highx[0]], + row1[lowx[1]], + row1[highx[1]]); + + a = _mm_unpacklo_epi8(lo, zero); + b = _mm_unpackhi_epi8(lo, zero); + c = _mm_unpacklo_epi8(hi, zero); + d = _mm_unpackhi_epi8(hi, zero); + + a = _mm_mullo_epi16(a, vertmul); + b = _mm_mullo_epi16(b, vertmul); + c = _mm_mullo_epi16(c, vertmul2); + d = _mm_mullo_epi16(d, vertmul2); + + a = _mm_add_epi16(a, c); + a = _mm_srli_epi16(a, 8); + b = _mm_add_epi16(b, d); + b = _mm_srli_epi16(b, 8); + + a = _mm_mullo_epi16(a, horzmul); + b = _mm_mullo_epi16(b, horzmul2); + + lo = _mm_srli_si128(a, 8); + a = _mm_and_si128(a, low); + a = _mm_add_epi16(a, lo); + + hi = _mm_slli_si128(b, 8); + b = _mm_and_si128(b, high); + b = _mm_add_epi16(b, hi); + + a = _mm_add_epi16(a, b); + a = _mm_srli_epi16(a, 8); + + a = _mm_packus_epi16(a, zero); + + _mm_storel_epi64((__m128i *) &dst[x * 4], a); + } + + for (; x < tgtw; x++) { + // Remainder in C + const float nx = x * invdiff; + const uint16_t lowx = nx; + const uint16_t highx = lowx + 1; + const uint16_t right = (nx - lowx) * 256; + const uint16_t left = 256 - right; + + uint8_t i; + uint32_t val, val2; + for (i = 0; i < 4; i++) { + val = brow0[lowx * 4 + i] * left; + val += brow0[highx * 4 + i] * right; + val >>= 8; + + val2 = brow1[lowx * 4 + i] * left; + val2 += brow1[highx * 4 + i] * right; + val2 >>= 8; + + dst[x * 4 + i] = + (val * top + val2 * bot) >> 8; + } + } + } +} + +}; // namespace rfb diff --git a/common/rfb/scale_sse2.h b/common/rfb/scale_sse2.h new file mode 100644 index 0000000..e60357d --- /dev/null +++ b/common/rfb/scale_sse2.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2021 Kasm Web + * + * 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 __RFB_SCALE_SSE2_H__ +#define __RFB_SCALE_SSE2_H__ + +#include + +namespace rfb { + + void SSE2_halve(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride); + + void SSE2_scale(const uint8_t *oldpx, + const uint16_t tgtw, const uint16_t tgth, + uint8_t *newpx, + const unsigned oldstride, const unsigned newstride, + const float tgtdiff); +}; + +#endif diff --git a/kasmweb b/kasmweb index ba40cac..e0bb9f6 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit ba40cacce068fa35fc706c41605db14c04348170 +Subproject commit e0bb9f6bcf945da6cb10fd0eb48b63b48bf09bb8 From 159d7527955f131e096cf1602b7f9f66cc5d66cb Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 10 Sep 2021 18:11:35 +0000 Subject: [PATCH 03/22] Update noVNC commit --- kasmweb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb b/kasmweb index e0bb9f6..d1e4bda 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit e0bb9f6bcf945da6cb10fd0eb48b63b48bf09bb8 +Subproject commit d1e4bda4b3ed118ed90752ffe0f08b9ead0851c9 From 3f680a7b803e23a44b434f4d2604c19324ce2f74 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 23 Sep 2021 14:04:19 -0400 Subject: [PATCH 04/22] KASM-1913 webp mode tweaks (#55) Co-authored-by: matt --- common/rfb/TightWEBPEncoder.cxx | 18 +++++++++--------- kasmweb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/common/rfb/TightWEBPEncoder.cxx b/common/rfb/TightWEBPEncoder.cxx index 4953ff4..74c109c 100644 --- a/common/rfb/TightWEBPEncoder.cxx +++ b/common/rfb/TightWEBPEncoder.cxx @@ -71,12 +71,12 @@ static const struct TightWEBPConfiguration conf[10] = { { 24, 0 }, // 1 { 30, 0 }, // 2 { 37, 0 }, // 3 - { 42, 1 }, // 4 - { 65, 1 }, // 5 - { 78, 1 }, // 6 - { 85, 2 }, // 7 - { 88, 3 }, // 8 - { 100, 4 } // 9 + { 42, 0 }, // 4 + { 65, 0 }, // 5 + { 78, 0 }, // 6 + { 85, 0 }, // 7 + { 88, 0 }, // 8 + { 100, 0 } // 9 }; @@ -143,7 +143,7 @@ void TightWEBPEncoder::compressOnly(const PixelBuffer* pb, const uint8_t quality method = conf[qualityIn].method; } else { quality = 8; - method = 4; + method = 0; } WebPConfigInit(&cfg); @@ -214,7 +214,7 @@ void TightWEBPEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) method = conf[qualityLevel].method; } else { quality = 8; - method = 4; + method = 0; } WebPConfigInit(&cfg); @@ -265,7 +265,7 @@ rdr::U32 TightWEBPEncoder::benchmark() const rdr::U8* buffer; struct timeval start; int stride, i; - const uint8_t quality = 8, method = 4; + const uint8_t quality = 8, method = 2; WebPConfig cfg; WebPPicture pic; WebPMemoryWriter wrt; diff --git a/kasmweb b/kasmweb index d1e4bda..a0907e9 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit d1e4bda4b3ed118ed90752ffe0f08b9ead0851c9 +Subproject commit a0907e9390e8a135a280f7e4157eced05b8ec687 From dd26c40844f6c1a19ce5c70e685e49abcd353049 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 23 Sep 2021 18:49:52 -0400 Subject: [PATCH 05/22] KASM-1970 fix broken pipeline (#56) Allow jobs to fail if they are not required for the greater KAsm. Later we should modify this to not allow a fail fo rrelease branches --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c8584fb..771a4ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,6 +24,7 @@ stages: build_ubuntu_bionic: stage: build + allow_failure: true before_script: - *prepare_build after_script: @@ -36,6 +37,7 @@ build_ubuntu_bionic: build_ubuntu_bionic_libjpeg_turbo: stage: build + allow_failure: false before_script: - *prepare_build after_script: @@ -48,6 +50,7 @@ build_ubuntu_bionic_libjpeg_turbo: build_ubuntu_focal: stage: build + allow_failure: true before_script: - *prepare_build after_script: @@ -60,6 +63,7 @@ build_ubuntu_focal: build_debian_buster: stage: build + allow_failure: true before_script: - *prepare_build after_script: @@ -72,6 +76,7 @@ build_debian_buster: build_debian_bullseye: stage: build + allow_failure: true before_script: - *prepare_build after_script: @@ -84,6 +89,7 @@ build_debian_bullseye: build_kali_rolling: stage: build + allow_failure: true before_script: - *prepare_build after_script: @@ -96,6 +102,7 @@ build_kali_rolling: build_centos7: stage: build + allow_failure: true before_script: - *prepare_build after_script: From 93e47e5d541e540459050da5626f8ab67739d486 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Fri, 24 Sep 2021 08:25:35 -0400 Subject: [PATCH 06/22] Bugfix/kasm 1912 websocket crash (#57) * Copy-paste bug in SSE2 scaling to under 0.5x * Better handling of websocket frames * KASM-1912 websocket crash, scaling bug Co-authored-by: Lauri Kasanen Co-authored-by: matt --- common/network/websockify.c | 23 +++++++++++++---------- common/rfb/EncodeManager.cxx | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/common/network/websockify.c b/common/network/websockify.c index a97c189..f0a322a 100644 --- a/common/network/websockify.c +++ b/common/network/websockify.c @@ -44,11 +44,11 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) { int maxfd, client = ws_ctx->sockfd; unsigned int opcode, left, ret; unsigned int tout_start, tout_end, cout_start, cout_end; - unsigned int tin_start, tin_end; + unsigned int tin_end; ssize_t len, bytes; tout_start = tout_end = cout_start = cout_end = - tin_start = tin_end = 0; + tin_end = 0; maxfd = client > target ? client+1 : target+1; while (1) { @@ -165,7 +165,7 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) { } if (FD_ISSET(client, &rlist)) { - bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1); + bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1-tin_end); if (pipe_error) { break; } if (bytes <= 0) { handler_emsg("client closed connection\n"); @@ -180,13 +180,13 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) { printf("\n"); */ if (ws_ctx->hybi) { - len = decode_hybi(ws_ctx->tin_buf + tin_start, - tin_end-tin_start, + len = decode_hybi(ws_ctx->tin_buf, + tin_end, ws_ctx->tout_buf, BUFSIZE-1, &opcode, &left); } else { - len = decode_hixie(ws_ctx->tin_buf + tin_start, - tin_end-tin_start, + len = decode_hixie(ws_ctx->tin_buf, + tin_end, ws_ctx->tout_buf, BUFSIZE-1, &opcode, &left); } @@ -208,10 +208,13 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) { break; } if (left) { - tin_start = tin_end - left; - //printf("partial frame from client"); + const unsigned tin_start = tin_end - left; + //handler_emsg("partial frame from client, %u left, start %u end %u, lens %lu %lu\n", + // left, tin_start, tin_end, bytes, len); + memmove(ws_ctx->tin_buf, ws_ctx->tin_buf + tin_start, left); + tin_end = left; } else { - tin_start = 0; + //handler_emsg("handled %lu/%lu bytes\n", bytes, len); tin_end = 0; } diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index 30489c7..9a079bc 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -1024,7 +1024,7 @@ PixelBuffer *rfb::progressiveBilinearScale(const PixelBuffer *pb, rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(), &newstride); - SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff); + SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtw / (float) oldw); if (del) delete pb; } From 5a1f9b9ae27d9a537d170f18503caea97dd341a5 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Wed, 29 Sep 2021 13:47:14 -0400 Subject: [PATCH 07/22] Update README download link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4356ecb..a618547 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Future Goals: #### Debian-based ```sh -wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.1-beta/kasmvncserver_0.9.1~beta-1_amd64.deb +wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb sudo dpkg -i kasmvncserver_0.9.1~beta-1_amd64.deb sudo apt-get -f install From e470021ca65e4b7c988a14aacfaf8cfe3789f1d1 Mon Sep 17 00:00:00 2001 From: Justin Travis Date: Sun, 3 Oct 2021 13:33:04 -0400 Subject: [PATCH 08/22] KASM-1985 Address build failures by updating Centos CA certs --- builder/dockerfile.centos_core.build | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/dockerfile.centos_core.build b/builder/dockerfile.centos_core.build index 4574075..c277e1a 100644 --- a/builder/dockerfile.centos_core.build +++ b/builder/dockerfile.centos_core.build @@ -3,6 +3,7 @@ FROM centos:centos7 ENV KASMVNC_BUILD_OS centos ENV KASMVNC_BUILD_OS_CODENAME core +RUN yum update -y ca-certificates RUN yum install -y build-dep xorg-server libxfont-dev sudo RUN yum install -y gcc cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver RUN yum install -y libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev openssl-devel From 3f3d31312c68f485303b362b3ff13df230a93276 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Mon, 4 Oct 2021 10:48:14 -0400 Subject: [PATCH 09/22] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a618547..6d13c51 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Future Goals: ```sh wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb -sudo dpkg -i kasmvncserver_0.9.1~beta-1_amd64.deb +sudo dpkg -i kasmvncserver_*.deb sudo apt-get -f install # We provide an example script to run KasmVNC at # From 0a45fcc700e848146794abf46d6a90fad72bb61a Mon Sep 17 00:00:00 2001 From: Mariusz Marciniak Date: Thu, 16 Sep 2021 04:10:19 -0700 Subject: [PATCH 10/22] KASM-1871 Add smooth scrolling Previously all scrolling relied on "clicking" the up/down or left/right scroll buttons which made it unprecise and to always scroll at the same speed. Now we pass the scroll delta directly to the xorg input driver so the scroll is more responsinve and adaptive. --- .gitmodules | 2 +- common/rfb/InputHandler.h | 4 +++- common/rfb/SMsgReader.cxx | 4 +++- common/rfb/VNCSConnectionST.cxx | 4 ++-- common/rfb/VNCSConnectionST.h | 2 +- kasmweb | 2 +- unix/xserver/hw/vnc/Input.c | 30 +++++++++++++++++++++++++-- unix/xserver/hw/vnc/Input.h | 1 + unix/xserver/hw/vnc/XserverDesktop.cc | 11 ++++++---- unix/xserver/hw/vnc/XserverDesktop.h | 2 +- 10 files changed, 48 insertions(+), 14 deletions(-) diff --git a/.gitmodules b/.gitmodules index e43a13a..bcb6a03 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "kasmweb"] path = kasmweb url = https://github.com/kasmtech/noVNC.git - branch = master + branch = feature/KASM-1871_scroll_sensitivity diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h index 806625f..e16a810 100644 --- a/common/rfb/InputHandler.h +++ b/common/rfb/InputHandler.h @@ -38,7 +38,9 @@ namespace rfb { virtual void pointerEvent(const Point& __unused_attr pos, int __unused_attr buttonMask, const bool __unused_attr skipClick, - const bool __unused_attr skipRelease) { } + const bool __unused_attr skipRelease, + int scrollX, + int scrollY) { } virtual void clientCutText(const char* __unused_attr str, int __unused_attr len) { } }; diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index de5e1b3..b9d3491 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -223,7 +223,9 @@ void SMsgReader::readPointerEvent() int mask = is->readU8(); int x = is->readU16(); int y = is->readU16(); - handler->pointerEvent(Point(x, y), mask, false, false); + int scrollX = is->readS16(); + int scrollY = is->readS16(); + handler->pointerEvent(Point(x, y), mask, false, false, scrollX, scrollY); } diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index bd24229..10e265c 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -692,7 +692,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) setCursor(); } -void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease) +void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY) { pointerEventTime = lastEventTime = time(0); server->lastUserInputTime = lastEventTime; @@ -720,7 +720,7 @@ void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool } } - server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease); + server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease, scrollX, scrollY); } } diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 86c99c6..4262337 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -204,7 +204,7 @@ namespace rfb { virtual void queryConnection(const char* userName); virtual void clientInit(bool shared); virtual void setPixelFormat(const PixelFormat& pf); - virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease); + virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual void framebufferUpdateRequest(const Rect& r, bool incremental); virtual void setDesktopSize(int fb_width, int fb_height, diff --git a/kasmweb b/kasmweb index a0907e9..d20e5d7 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit a0907e9390e8a135a280f7e4157eced05b8ec687 +Subproject commit d20e5d78fac5a8866dba5bff2585cd4bb75f7fc9 diff --git a/unix/xserver/hw/vnc/Input.c b/unix/xserver/hw/vnc/Input.c index f8ac3fd..e8f9170 100644 --- a/unix/xserver/hw/vnc/Input.c +++ b/unix/xserver/hw/vnc/Input.c @@ -234,6 +234,14 @@ void vncPointerMove(int x, int y) cursorPosY = y; } +void vncScroll(int x, int y) { + ValuatorMask mask; + valuator_mask_zero(&mask); + valuator_mask_set(&mask, 2, x); + valuator_mask_set(&mask, 3, y); + QueuePointerEvents(vncPointerDev, MotionNotify, 0, POINTER_RELATIVE, &mask); +} + void vncGetPointerPos(int *x, int *y) { if (vncPointerDev != NULL) { @@ -261,7 +269,7 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff) * is not a bug. */ Atom btn_labels[BUTTONS]; - Atom axes_labels[2]; + Atom axes_labels[4]; switch (onoff) { case DEVICE_INIT: @@ -278,11 +286,29 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff) axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); + axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL); + axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL); + InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels, (PtrCtrlProcPtr)NoopDDA, GetMotionHistorySize(), - 2, axes_labels); + 4, axes_labels); + + InitValuatorAxisStruct(pDevice, 2, axes_labels[2], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative); + InitValuatorAxisStruct(pDevice, 3, axes_labels[3], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative); + + char* envScrollFactorH = getenv("SCROLL_FACTOR_H"); + char* envScrollFactorV = getenv("SCROLL_FACTOR_V"); + + float scrollFactorH = envScrollFactorH ? atof(envScrollFactorH) : 50.0; + float scrollFactorV = envScrollFactorV ? atof(envScrollFactorV) : 50.0; + + LOG_INFO("Mouse horizonatl scroll factor: %f", scrollFactorH); + LOG_INFO("Mouse vertical scroll factor: %f", scrollFactorV); + + SetScrollValuator(pDevice, 2, SCROLL_TYPE_HORIZONTAL, scrollFactorH, SCROLL_FLAG_NONE); + SetScrollValuator(pDevice, 3, SCROLL_TYPE_VERTICAL, scrollFactorV, SCROLL_FLAG_PREFERRED); break; case DEVICE_ON: pDev->on = TRUE; diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h index 76680c0..ddc4e06 100644 --- a/unix/xserver/hw/vnc/Input.h +++ b/unix/xserver/hw/vnc/Input.h @@ -35,6 +35,7 @@ void vncInitInputDevice(void); void vncPointerButtonAction(int buttonMask, const unsigned char skipclick, const unsigned char skiprelease); void vncPointerMove(int x, int y); +void vncScroll(int x, int y); void vncGetPointerPos(int *x, int *y); void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down); diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 1e9d663..a892128 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -446,11 +446,14 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept, void XserverDesktop::pointerEvent(const Point& pos, int buttonMask, - const bool skipClick, const bool skipRelease) + const bool skipClick, const bool skipRelease, int scrollX, int scrollY) { - vncPointerMove(pos.x + vncGetScreenX(screenIndex), - pos.y + vncGetScreenY(screenIndex)); - vncPointerButtonAction(buttonMask, skipClick, skipRelease); + if (scrollX == 0 && scrollY == 0) { + vncPointerMove(pos.x + vncGetScreenX(screenIndex), pos.y + vncGetScreenY(screenIndex)); + vncPointerButtonAction(buttonMask, skipClick, skipRelease); + } else { + vncScroll(scrollX, scrollY); + } } unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 014a48e..70394fa 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -90,7 +90,7 @@ public: // rfb::SDesktop callbacks virtual void pointerEvent(const rfb::Point& pos, int buttonMask, - const bool skipClick, const bool skipRelease); + const bool skipClick, const bool skipRelease, int scrollX = 0, int scrollY = 0); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); From 24e21325ef3a68cb88059b9691888e0054f5f7f4 Mon Sep 17 00:00:00 2001 From: Justin Travis Date: Tue, 12 Oct 2021 13:37:40 -0400 Subject: [PATCH 11/22] KASM-1871 Pull in submodule from tip of master --- .gitmodules | 2 +- kasmweb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index bcb6a03..e43a13a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "kasmweb"] path = kasmweb url = https://github.com/kasmtech/noVNC.git - branch = feature/KASM-1871_scroll_sensitivity + branch = master diff --git a/kasmweb b/kasmweb index d20e5d7..a4cf389 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit d20e5d78fac5a8866dba5bff2585cd4bb75f7fc9 +Subproject commit a4cf38902911e9ff07e9d3eb7909c5ba932a5089 From 620dbe558cfd3369630bcd64a035b6f658eeab3c Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 14 Oct 2021 08:23:00 -0400 Subject: [PATCH 12/22] [skip CI] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d13c51..65d84f4 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Future Goals: #### Debian-based ```sh -wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb +wget https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb sudo dpkg -i kasmvncserver_*.deb sudo apt-get -f install From e6d1b8c3c81390059f185ed54d54c07af6d9a0c6 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Thu, 14 Oct 2021 13:57:59 -0400 Subject: [PATCH 13/22] Fix a nonexistent username + empty password passing through (#68) Co-authored-by: Lauri Kasanen --- common/network/websocket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/network/websocket.c b/common/network/websocket.c index 47ac11d..e9ad04a 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -1320,6 +1320,8 @@ ws_ctx_t *do_handshake(int sock) { } else { // Client tried an empty password, just fail them response[0] = '\0'; + authbuf[0] = 'a'; + authbuf[1] = '\0'; } } From 5b28a168d00218156cc924ae2964ce536c01ede1 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Fri, 15 Oct 2021 15:57:58 -0400 Subject: [PATCH 14/22] Chromeclip (#69) * Initial binary clipboard support * Rename -DLP_Clip_Types to -DLP_ClipTypes * Better handling of websocket frames * Copy-paste bug in SSE2 scaling to under 0.5x * Remove old text clipboard * Bind text to binary clipboard * Move binclip clear to probing phase * Off-by-one in sse2 scaling * Add a clarifying log message for INCR clipboard transfers * WIP: Update novnc commit * Fix CentOS pipeline * webpack fix * Update novnc commit * Change some DLP defaults * update novnc commit Co-authored-by: Lauri Kasanen Co-authored-by: matt --- common/rfb/SConnection.cxx | 111 +--------- common/rfb/SConnection.h | 44 +--- common/rfb/SDesktop.h | 14 +- common/rfb/SMsgHandler.cxx | 18 +- common/rfb/SMsgHandler.h | 12 +- common/rfb/SMsgReader.cxx | 132 ++++-------- common/rfb/SMsgReader.h | 1 + common/rfb/SMsgWriter.cxx | 117 +--------- common/rfb/SMsgWriter.h | 12 +- common/rfb/ServerCore.cxx | 10 +- common/rfb/ServerCore.h | 1 + common/rfb/VNCSConnectionST.cxx | 93 ++++---- common/rfb/VNCSConnectionST.h | 13 +- common/rfb/VNCServer.h | 11 - common/rfb/VNCServerST.cxx | 53 +++-- common/rfb/VNCServerST.h | 11 +- common/rfb/msgTypes.h | 2 + common/rfb/scale_sse2.cxx | 4 +- kasmweb | 2 +- unix/xserver/hw/vnc/XserverDesktop.cc | 40 ++-- unix/xserver/hw/vnc/XserverDesktop.h | 9 +- unix/xserver/hw/vnc/Xvnc.man | 11 +- unix/xserver/hw/vnc/vncExtInit.cc | 68 +++++- unix/xserver/hw/vnc/vncExtInit.h | 14 +- unix/xserver/hw/vnc/vncSelection.c | 299 ++++++++++++++++++-------- unix/xserver/hw/vnc/vncSelection.h | 3 +- 26 files changed, 513 insertions(+), 592 deletions(-) diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index bd1ea45..5567a96 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -284,74 +284,26 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) } SMsgHandler::setEncodings(nEncodings, encodings); - - if (cp.supportsExtendedClipboard) { - rdr::U32 sizes[] = { 0 }; - writer()->writeClipboardCaps(rfb::clipboardUTF8 | - rfb::clipboardRequest | - rfb::clipboardPeek | - rfb::clipboardNotify | - rfb::clipboardProvide, - sizes); - } } -void SConnection::clientCutText(const char* str, int len) +void SConnection::clearBinaryClipboard() { - hasLocalClipboard = false; - - strFree(clientClipboard); - clientClipboard = NULL; - - clientClipboard = latin1ToUTF8(str); - - handleClipboardAnnounce(true); -} - -void SConnection::handleClipboardRequest(rdr::U32 flags) -{ - if (!(flags & rfb::clipboardUTF8)) - return; - if (!hasLocalClipboard) - return; - handleClipboardRequest(); + binaryClipboard.clear(); } -void SConnection::handleClipboardPeek(rdr::U32 flags) +void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len) { - if (!hasLocalClipboard) - return; - if (cp.clipboardFlags() & rfb::clipboardNotify) - writer()->writeClipboardNotify(rfb::clipboardUTF8); -} + binaryClipboard_t bin; + strncpy(bin.mime, mime, sizeof(bin.mime)); + bin.mime[sizeof(bin.mime) - 1] = '\0'; -void SConnection::handleClipboardNotify(rdr::U32 flags) -{ - strFree(clientClipboard); - clientClipboard = NULL; + bin.data.resize(len); + memcpy(&bin.data[0], data, len); - if (flags & rfb::clipboardUTF8) { - handleClipboardAnnounce(true); - hasLocalClipboard = false; - } else { - handleClipboardAnnounce(false); - } + binaryClipboard.push_back(bin); } -void SConnection::handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) -{ - if (!(flags & rfb::clipboardUTF8)) - return; - - strFree(clientClipboard); - clientClipboard = NULL; - - clientClipboard = convertLF((const char*)data[0], lengths[0]); - - handleClipboardData(clientClipboard, strlen(clientClipboard)); -} void SConnection::supportsQEMUKeyEvent() { @@ -445,56 +397,13 @@ void SConnection::enableContinuousUpdates(bool enable, { } -void SConnection::handleClipboardRequest() -{ -} - void SConnection::handleClipboardAnnounce(bool available) { } -void SConnection::handleClipboardData(const char* data, int len) -{ -} - -void SConnection::requestClipboard() -{ - if (clientClipboard != NULL) { - handleClipboardData(clientClipboard, strlen(clientClipboard)); - return; - } - - if (cp.supportsExtendedClipboard && - (cp.clipboardFlags() & rfb::clipboardRequest)) - writer()->writeClipboardRequest(rfb::clipboardUTF8); -} - void SConnection::announceClipboard(bool available) { hasLocalClipboard = available; - - if (cp.supportsExtendedClipboard && - (cp.clipboardFlags() & rfb::clipboardNotify)) - writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); - else { - if (available) - handleClipboardRequest(); - } -} - -void SConnection::sendClipboardData(const char* data, int len) -{ - if (cp.supportsExtendedClipboard && - (cp.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()->writeServerCutText(latin1.buf, strlen(latin1.buf)); - } } void SConnection::writeFakeColourMap(void) diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 811ba04..8b5b3de 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace rfb { @@ -73,14 +74,9 @@ namespace rfb { virtual void setEncodings(int nEncodings, const rdr::S32* encodings); - virtual void clientCutText(const char* str, int len); - - 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); + virtual void clearBinaryClipboard(); + virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len); virtual void supportsQEMUKeyEvent(); @@ -127,25 +123,11 @@ namespace rfb { virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); - // handleClipboardRequest() is called whenever the client requests - // the server to send over its clipboard data. It will only be - // called after the server has first announced a clipboard change - // via announceClipboard(). - virtual void handleClipboardRequest(); - // handleClipboardAnnounce() is called to indicate a change in the // clipboard on the client. Call requestClipboard() to access the // actual data. virtual void handleClipboardAnnounce(bool available); - // handleClipboardData() is called when the client 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 - // client received the request. - virtual void handleClipboardData(const char* data, int len); - - virtual void add_changed_all() {} // setAccessRights() allows a security package to limit the access rights @@ -166,22 +148,11 @@ namespace rfb { // Other methods - // requestClipboard() will result in a request to the client to - // transfer its clipboard data. A call to handleClipboardData() - // will be made once the data is available. - virtual void requestClipboard(); - // announceClipboard() informs the client of changes to the // clipboard on the server. The client may later request the // clipboard data via handleClipboardRequest(). virtual void announceClipboard(bool available); - // sendClipboardData() transfers the clipboard data to the client - // and should be called whenever the client has requested the - // clipboard via handleClipboardRequest(). - virtual void sendClipboardData(const char* data, int len); - - // authenticated() returns true if the client has authenticated // successfully. bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || @@ -221,12 +192,19 @@ namespace rfb { rdr::S32 getPreferredEncoding() { return preferredEncoding; } + struct binaryClipboard_t { + char mime[32]; + std::vector data; + }; + protected: void setState(stateEnum s) { state_ = s; } void setReader(SMsgReader *r) { reader_ = r; } void setWriter(SMsgWriter *w) { writer_ = w; } + std::vector binaryClipboard; + private: void writeFakeColourMap(void); diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index ec833c3..5696cfd 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -78,23 +78,13 @@ namespace rfb { // the relevant RFB protocol messages from clients. // See InputHandler for method signatures. - // handleClipboardRequest() is called whenever a client requests - // the server to send over its clipboard data. It will only be - // called after the server has first announced a clipboard change - // via VNCServer::announceClipboard(). - virtual void handleClipboardRequest() {} - // handleClipboardAnnounce() is called to indicate a change in the // clipboard on a client. Call VNCServer::requestClipboard() to // access the actual data. virtual void handleClipboardAnnounce(bool __unused_attr available) {} - // handleClipboardData() is called when a client has sent over - // the clipboard data as a result of a previous call to - // VNCServer::requestClipboard(). Note that this function might - // never be called if the clipboard data was no longer available - // when the client received the request. - virtual void handleClipboardData(const char* __unused_attr data, int len __unused_attr) {} + virtual void handleClipboardAnnounceBinary(const unsigned __unused_attr num, + const char __unused_attr mimes[][32]) {} protected: virtual ~SDesktop() {} diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx index 29c33ea..07499bc 100644 --- a/common/rfb/SMsgHandler.cxx +++ b/common/rfb/SMsgHandler.cxx @@ -64,26 +64,16 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) supportsQEMUKeyEvent(); } -void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) +void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32]) { - cp.setClipboardCaps(flags, lengths); } -void SMsgHandler::handleClipboardRequest(rdr::U32 flags) +void SMsgHandler::clearBinaryClipboard() { } -void SMsgHandler::handleClipboardPeek(rdr::U32 flags) -{ -} - -void SMsgHandler::handleClipboardNotify(rdr::U32 flags) -{ -} - -void SMsgHandler::handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) +void SMsgHandler::addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len) { } diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index d2fe4af..d9d852a 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -54,14 +54,10 @@ namespace rfb { virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h) = 0; - 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); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); + virtual void clearBinaryClipboard(); + virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len); virtual void sendStats(const bool toClient = true) = 0; virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0; diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index b9d3491..3719756 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -35,8 +35,6 @@ using namespace rfb; static LogWriter vlog("SMsgReader"); -static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024); - SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_) : handler(handler_), is(is_) { @@ -83,6 +81,9 @@ void SMsgReader::readMsg() case msgTypeFrameStats: readFrameStats(); break; + case msgTypeBinaryClipboard: + readBinaryClipboard(); + break; case msgTypeKeyEvent: readKeyEvent(); break; @@ -240,109 +241,54 @@ void SMsgReader::readClientCutText() readExtendedClipboard(slen); return; } - if (len > (size_t)maxCutText) { - is->skip(len); - vlog.error("Cut text too long (%d bytes) - ignoring", len); - return; - } - CharArray ca(len+1); - ca.buf[len] = 0; - is->readBytes(ca.buf, len); - handler->clientCutText(ca.buf, len); + is->skip(len); + vlog.error("Client sent old cuttext msg, ignoring"); } -void SMsgReader::readExtendedClipboard(rdr::S32 len) +void SMsgReader::readBinaryClipboard() { - 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; + const rdr::U8 num = is->readU8(); + rdr::U8 i, valid = 0; + char tmpmimes[num][32]; + + handler->clearBinaryClipboard(); + for (i = 0; i < num; i++) { + const rdr::U8 mimelen = is->readU8(); + if (mimelen > 32 - 1) { + vlog.error("Mime too long (%u)", mimelen); + } - if (action & clipboardCaps) { - int i; - size_t num; - rdr::U32 lengths[16]; + char mime[mimelen + 1]; + mime[mimelen] = '\0'; + is->readBytes(mime, mimelen); - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - num++; - } + strncpy(tmpmimes[valid], mime, 32); + tmpmimes[valid][31] = '\0'; - if (len < (rdr::S32)(4 + 4*num)) - throw Exception("Invalid extended clipboard message"); + const rdr::U32 len = is->readU32(); + CharArray ca(len); + is->readBytes(ca.buf, len); - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - lengths[num++] = is->readU32(); + if (rfb::Server::DLP_ClipAcceptMax && len > (unsigned) rfb::Server::DLP_ClipAcceptMax) { + vlog.info("DLP: refused to receive binary clipboard, too large"); + continue; } - 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++; - } + vlog.debug("Received binary clipboard, type %s, %u bytes", mime, len); - zis.flushUnderlying(); - zis.setUnderlying(NULL, 0); + handler->addBinaryClipboard(mime, (rdr::U8 *) ca.buf, len); + valid++; + } - handler->handleClipboardProvide(flags, lengths, buffers); + handler->handleClipboardAnnounceBinary(valid, tmpmimes); +} - 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 SMsgReader::readExtendedClipboard(rdr::S32 len) +{ + if (len < 4) + throw Exception("Invalid extended clipboard message"); + vlog.error("Client sent old cuttext msg, ignoring"); + is->skip(len); } void SMsgReader::readRequestStats() diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h index a9b09cc..8a1eb34 100644 --- a/common/rfb/SMsgReader.h +++ b/common/rfb/SMsgReader.h @@ -58,6 +58,7 @@ namespace rfb { void readExtendedClipboard(rdr::S32 len); void readRequestStats(); void readFrameStats(); + void readBinaryClipboard(); void readQEMUMessage(); void readQEMUKeyEvent(); diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index a7c12f4..a0a9df0 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -85,118 +85,21 @@ void SMsgWriter::writeBell() endMsg(); } -void SMsgWriter::writeServerCutText(const char* str, int len) +void SMsgWriter::writeBinaryClipboard(const std::vector &b) { - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeU32(len); - os->writeBytes(str, len); - endMsg(); -} - -void SMsgWriter::writeClipboardCaps(rdr::U32 caps, - const rdr::U32* lengths) -{ - size_t i, count; - - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - - count = 0; - for (i = 0;i < 16;i++) { - if (caps & (1 << i)) - count++; - } - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-(4 + 4 * count)); - - os->writeU32(caps | clipboardCaps); - - count = 0; - for (i = 0;i < 16;i++) { - if (caps & (1 << i)) - os->writeU32(lengths[count++]); - } - - endMsg(); -} - -void SMsgWriter::writeClipboardRequest(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardRequest)) - throw Exception("Client does not support clipboard \"request\" action"); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardRequest); - endMsg(); -} + startMsg(msgTypeBinaryClipboard); -void SMsgWriter::writeClipboardPeek(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardPeek)) - throw Exception("Client does not support clipboard \"peek\" action"); + os->writeU8(b.size()); + rdr::U8 i; + for (i = 0; i < b.size(); i++) { + const rdr::U8 mimelen = strlen(b[i].mime); + os->writeU8(mimelen); + os->writeBytes(b[i].mime, mimelen); - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardPeek); - endMsg(); -} - -void SMsgWriter::writeClipboardNotify(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardNotify)) - throw Exception("Client does not support clipboard \"notify\" action"); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardNotify); - endMsg(); -} - -void SMsgWriter::writeClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) -{ - rdr::MemOutStream mos; - rdr::ZlibOutStream zos; - - int i, count; - - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardProvide)) - throw Exception("Client does not support clipboard \"provide\" action"); - - zos.setUnderlying(&mos); - - count = 0; - for (i = 0;i < 16;i++) { - if (!(flags & (1 << i))) - continue; - zos.writeU32(lengths[count]); - zos.writeBytes(data[count], lengths[count]); - count++; + os->writeU32(b[i].data.size()); + os->writeBytes(&b[i].data[0], b[i].data.size()); } - zos.flush(); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-(4 + mos.length())); - os->writeU32(flags | clipboardProvide); - os->writeBytes(mos.data(), mos.length()); endMsg(); } diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 0313bcc..c72012e 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include namespace rdr { class OutStream; } @@ -54,14 +56,8 @@ namespace rfb { // writeBell() and writeServerCutText() do the obvious thing. void writeBell(); - void writeServerCutText(const char* str, int len); - - void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths); - void writeClipboardRequest(rdr::U32 flags); - void writeClipboardPeek(rdr::U32 flags); - void writeClipboardNotify(rdr::U32 flags); - void writeClipboardProvide(rdr::U32 flags, const size_t* lengths, - const rdr::U8* const* data); + + void writeBinaryClipboard(const std::vector &b); void writeStats(const char* str, int len); diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 28ce777..1d568c1 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -149,15 +149,15 @@ rfb::IntParameter rfb::Server::webpVideoQuality rfb::IntParameter rfb::Server::DLP_ClipSendMax ("DLP_ClipSendMax", "Limit clipboard bytes to send to clients in one transaction", - 10000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_ClipAcceptMax ("DLP_ClipAcceptMax", "Limit clipboard bytes to receive from clients in one transaction", - 10000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_ClipDelay ("DLP_ClipDelay", "This many milliseconds must pass between clipboard actions", - 1000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_KeyRateLimit ("DLP_KeyRateLimit", "Reject keyboard presses over this many per second", @@ -171,6 +171,10 @@ rfb::StringParameter rfb::Server::DLP_Region ("DLP_Region", "Black out anything outside this region", ""); +rfb::StringParameter rfb::Server::DLP_Clip_Types +("DLP_ClipTypes", + "Allowed binary clipboard mimetypes", + "text/html,image/png"); rfb::BoolParameter rfb::Server::DLP_RegionAllowClick ("DLP_RegionAllowClick", diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index 766eda0..70075bf 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -50,6 +50,7 @@ namespace rfb { static IntParameter DLP_KeyRateLimit; static StringParameter DLP_ClipLog; static StringParameter DLP_Region; + static StringParameter DLP_Clip_Types; static BoolParameter DLP_RegionAllowClick; static BoolParameter DLP_RegionAllowRelease; static IntParameter jpegVideoQuality; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 10e265c..fa85a70 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -57,7 +57,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, inProcessMessages(false), pendingSyncFence(false), syncFence(false), fenceFlags(0), fenceDataLen(0), fenceData(NULL), congestionTimer(this), - losslessTimer(this), kbdLogTimer(this), server(server_), updates(false), + losslessTimer(this), kbdLogTimer(this), binclipTimer(this), + server(server_), updates(false), updateRenderedCursor(false), removeRenderedCursor(false), continuousUpdates(false), encodeManager(this, &server_->encCache), needsPermCheck(false), pointerEventTime(0), @@ -413,18 +414,6 @@ static void keylog(unsigned keysym, const char *client) { flushKeylog(client); } -void VNCSConnectionST::requestClipboardOrClose() -{ - try { - if (!(accessRights & AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; - if (state() != RFBSTATE_NORMAL) return; - requestClipboard(); - } catch(rdr::Exception& e) { - close(e.str()); - } -} - void VNCSConnectionST::announceClipboardOrClose(bool available) { try { @@ -437,29 +426,51 @@ void VNCSConnectionST::announceClipboardOrClose(bool available) } } -void VNCSConnectionST::sendClipboardDataOrClose(const char* data) +void VNCSConnectionST::clearBinaryClipboardData() +{ + clearBinaryClipboard(); +} + +void VNCSConnectionST::sendBinaryClipboardDataOrClose(const char* mime, + const unsigned char *data, + const unsigned len) { try { if (!(accessRights & AccessCutText)) return; if (!rfb::Server::sendCutText) return; - if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { - vlog.info("DLP: client %s: refused to send clipboard, too soon", + if (rfb::Server::DLP_ClipSendMax && len > (unsigned) rfb::Server::DLP_ClipSendMax) { + vlog.info("DLP: client %s: refused to send binary clipboard, too large", sock->getPeerAddress()); return; } - int len = strlen(data); - const int origlen = len; - if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax) - len = rfb::Server::DLP_ClipSendMax; - cliplog(data, len, origlen, "sent", sock->getPeerAddress()); + + cliplog((const char *) data, len, len, "sent", sock->getPeerAddress()); if (state() != RFBSTATE_NORMAL) return; - sendClipboardData(data, len); - gettimeofday(&lastClipboardOp, NULL); + + addBinaryClipboard(mime, data, len); + binclipTimer.start(100); } catch(rdr::Exception& e) { close(e.str()); } } +void VNCSConnectionST::getBinaryClipboardData(const char* mime, const unsigned char **data, + unsigned *len) +{ + unsigned i; + for (i = 0; i < binaryClipboard.size(); i++) { + if (!strcmp(binaryClipboard[i].mime, mime)) { + *data = &binaryClipboard[i].data[0]; + *len = binaryClipboard[i].data.size(); + return; + } + } + + vlog.error("Binary clipboard data for mime %s not found", mime); + *data = (const unsigned char *) ""; + *len = 1; +} + void VNCSConnectionST::setDesktopNameOrClose(const char *name) { try { @@ -1014,12 +1025,6 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, } } -void VNCSConnectionST::handleClipboardRequest() -{ - if (!(accessRights & AccessCutText)) return; - server->handleClipboardRequest(this); -} - void VNCSConnectionST::handleClipboardAnnounce(bool available) { if (!(accessRights & AccessCutText)) return; @@ -1027,25 +1032,13 @@ void VNCSConnectionST::handleClipboardAnnounce(bool available) server->handleClipboardAnnounce(this, available); } -void VNCSConnectionST::handleClipboardData(const char* data, int len) +void VNCSConnectionST::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { if (!(accessRights & AccessCutText)) return; if (!rfb::Server::acceptCutText) return; - if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { - vlog.info("DLP: client %s: refused to receive clipboard, too soon", - sock->getPeerAddress()); - return; - } - const int origlen = len; - if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax) - len = rfb::Server::DLP_ClipAcceptMax; - cliplog(data, len, origlen, "received", sock->getPeerAddress()); - - gettimeofday(&lastClipboardOp, NULL); - server->handleClipboardData(this, data, len); + server->handleClipboardAnnounceBinary(this, num, mimes); } - // supportsLocalCursor() is called whenever the status of // cp.supportsLocalCursor has changed. If the client does now support local // cursor, we make sure that the old server-side rendered cursor is cleaned up @@ -1089,6 +1082,8 @@ bool VNCSConnectionST::handleTimeout(Timer* t) writeFramebufferUpdate(); else if (t == &kbdLogTimer) flushKeylog(sock->getPeerAddress()); + else if (t == &binclipTimer) + writeBinaryClipboard(); } catch (rdr::Exception& e) { close(e.str()); } @@ -1446,6 +1441,18 @@ void VNCSConnectionST::writeDataUpdate() requested.clear(); } +void VNCSConnectionST::writeBinaryClipboard() +{ + if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { + vlog.info("DLP: client %s: refused to send binary clipboard, too soon", + sock->getPeerAddress()); + return; + } + + writer()->writeBinaryClipboard(binaryClipboard); + + gettimeofday(&lastClipboardOp, NULL); +} void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) { diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 4262337..63ce49c 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -77,9 +77,12 @@ namespace rfb { void bellOrClose(); void setDesktopNameOrClose(const char *name); void setLEDStateOrClose(unsigned int state); - void requestClipboardOrClose(); void announceClipboardOrClose(bool available); - void sendClipboardDataOrClose(const char* data); + void clearBinaryClipboardData(); + void sendBinaryClipboardDataOrClose(const char* mime, const unsigned char *data, + const unsigned len); + void getBinaryClipboardData(const char* mime, const unsigned char **data, + unsigned *len); // checkIdleTimeout() returns the number of milliseconds left until the // idle timeout expires. If it has expired, the connection is closed and @@ -212,9 +215,8 @@ namespace rfb { virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); - virtual void handleClipboardRequest(); virtual void handleClipboardAnnounce(bool available); - virtual void handleClipboardData(const char* data, int len); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); virtual void supportsLocalCursor(); virtual void supportsFence(); virtual void supportsContinuousUpdates(); @@ -260,6 +262,8 @@ namespace rfb { void writeNoDataUpdate(); void writeDataUpdate(); + void writeBinaryClipboard(); + void screenLayoutChange(rdr::U16 reason); void setCursor(); void setCursorPos(); @@ -282,6 +286,7 @@ namespace rfb { Timer congestionTimer; Timer losslessTimer; Timer kbdLogTimer; + Timer binclipTimer; VNCServerST* server; SimpleUpdateTracker updates; diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 54e3504..33b0533 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -52,22 +52,11 @@ namespace rfb { // getPixelBuffer() returns a pointer to the PixelBuffer object. virtual PixelBuffer* getPixelBuffer() const = 0; - // requestClipboard() will result in a request to a client to - // transfer its clipboard data. A call to - // SDesktop::handleClipboardData() will be made once the data is - // available. - virtual void requestClipboard() = 0; - // announceClipboard() informs all clients of changes to the // clipboard on the server. A client may later request the // clipboard data via SDesktop::handleClipboardRequest(). virtual void announceClipboard(bool available) = 0; - // sendClipboardData() transfers the clipboard data to a client - // and should be called whenever a client has requested the - // clipboard via SDesktop::handleClipboardRequest(). - virtual void sendClipboardData(const char* data) = 0; - // bell() tells the server that it should make all clients make a bell sound. virtual void bell() = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 3e87892..7807feb 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -518,14 +518,6 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout) } } -void VNCServerST::requestClipboard() -{ - if (clipboardClient == NULL) - return; - - clipboardClient->requestClipboard(); -} - void VNCServerST::announceClipboard(bool available) { std::list::iterator ci, ci_next; @@ -541,20 +533,31 @@ void VNCServerST::announceClipboard(bool available) } } -void VNCServerST::sendClipboardData(const char* data) +void VNCServerST::sendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len) { std::list::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->sendBinaryClipboardDataOrClose(mime, data, len); + } +} - if (strchr(data, '\r') != NULL) - throw Exception("Invalid carriage return in clipboard data"); +void VNCServerST::getBinaryClipboardData(const char* mime, const unsigned char **data, + unsigned *len) +{ + if (!clipboardClient) + return; + clipboardClient->getBinaryClipboardData(mime, data, len); +} - for (ci = clipboardRequestors.begin(); - ci != clipboardRequestors.end(); ci = ci_next) { +void VNCServerST::clearBinaryClipboardData() +{ + std::list::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; - (*ci)->sendClipboardDataOrClose(data); + (*ci)->clearBinaryClipboardData(); } - - clipboardRequestors.clear(); } void VNCServerST::bell() @@ -1198,13 +1201,6 @@ bool VNCServerST::getComparerState() return false; } -void VNCServerST::handleClipboardRequest(VNCSConnectionST* client) -{ - clipboardRequestors.push_back(client); - if (clipboardRequestors.size() == 1) - desktop->handleClipboardRequest(); -} - void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, bool available) { @@ -1218,11 +1214,10 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, desktop->handleClipboardAnnounce(available); } -void VNCServerST::handleClipboardData(VNCSConnectionST* client, - const char* data, int len) +void VNCServerST::handleClipboardAnnounceBinary(VNCSConnectionST* client, + const unsigned num, + const char mimes[][32]) { - if (client != clipboardClient) - return; - desktop->handleClipboardData(data, len); + clipboardClient = client; + desktop->handleClipboardAnnounceBinary(num, mimes); } - diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 26c9ef6..0eff2d1 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -96,9 +96,12 @@ namespace rfb { virtual void setPixelBuffer(PixelBuffer* pb); virtual void setScreenLayout(const ScreenSet& layout); virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; } - virtual void requestClipboard(); virtual void announceClipboard(bool available); - virtual void sendClipboardData(const char* data); + virtual void clearBinaryClipboardData(); + virtual void sendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len); + virtual void getBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len); virtual void add_changed(const Region ®ion); virtual void add_copied(const Region &dest, const Point &delta); virtual void setCursor(int width, int height, const Point& hotspot, @@ -191,9 +194,9 @@ namespace rfb { void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; } - void handleClipboardRequest(VNCSConnectionST* client); void handleClipboardAnnounce(VNCSConnectionST* client, bool available); - void handleClipboardData(VNCSConnectionST* client, const char* data, int len); + void handleClipboardAnnounceBinary(VNCSConnectionST* client, const unsigned num, + const char mimes[][32]); protected: diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h index 5070c3f..ea8e007 100644 --- a/common/rfb/msgTypes.h +++ b/common/rfb/msgTypes.h @@ -31,6 +31,7 @@ namespace rfb { // kasm const int msgTypeStats = 178; const int msgTypeRequestFrameStats = 179; + const int msgTypeBinaryClipboard = 180; const int msgTypeServerFence = 248; @@ -49,6 +50,7 @@ namespace rfb { // kasm const int msgTypeRequestStats = 178; const int msgTypeFrameStats = 179; + //const int msgTypeBinaryClipboard = 180; const int msgTypeClientFence = 248; diff --git a/common/rfb/scale_sse2.cxx b/common/rfb/scale_sse2.cxx index e4c717b..d4110a6 100644 --- a/common/rfb/scale_sse2.cxx +++ b/common/rfb/scale_sse2.cxx @@ -69,7 +69,7 @@ void SSE2_halve(const uint8_t *oldpx, uint8_t * const dst = newpx + newstride * (y / 2) * 4; - for (x = 0; x < srcw; x += 4) { + for (x = 0; x < srcw - 3; x += 4) { __m128i lo, hi, a, b, c, d; lo = _mm_loadu_si128((__m128i *) &row0[x * 4]); hi = _mm_loadu_si128((__m128i *) &row1[x * 4]); @@ -141,7 +141,7 @@ void SSE2_scale(const uint8_t *oldpx, const __m128i vertmul = _mm_set1_epi16(top); const __m128i vertmul2 = _mm_set1_epi16(bot); - for (x = 0; x < tgtw; x += 2) { + for (x = 0; x < tgtw - 1; x += 2) { const float nx[2] = { x * invdiff, (x + 1) * invdiff, diff --git a/kasmweb b/kasmweb index a4cf389..0bd3813 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit a4cf38902911e9ff07e9d3eb7909c5ba932a5089 +Subproject commit 0bd38139494ef1bbaf580c2f4892c7be3a291b19 diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index a892128..26a9bfd 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -176,30 +176,43 @@ XserverDesktop::queryConnection(network::Socket* sock, return rfb::VNCServerST::PENDING; } -void XserverDesktop::requestClipboard() +void XserverDesktop::announceClipboard(bool available) { try { - server->requestClipboard(); + server->announceClipboard(available); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::requestClipboard: %s",e.str()); + vlog.error("XserverDesktop::announceClipboard: %s",e.str()); } } -void XserverDesktop::announceClipboard(bool available) +void XserverDesktop::clearBinaryClipboardData() { try { - server->announceClipboard(available); + server->clearBinaryClipboardData(); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::announceClipboard: %s",e.str()); + vlog.error("XserverDesktop::clearBinaryClipboardData: %s",e.str()); } } -void XserverDesktop::sendClipboardData(const char* data) +void XserverDesktop::sendBinaryClipboardData(const char* mime, + const unsigned char *data, + const unsigned len) { try { - server->sendClipboardData(data); + server->sendBinaryClipboardData(mime, data, len); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::sendClipboardData: %s",e.str()); + vlog.error("XserverDesktop::sendBinaryClipboardData: %s",e.str()); + } +} + +void XserverDesktop::getBinaryClipboardData(const char* mime, + const unsigned char **data, + unsigned *len) +{ + try { + server->getBinaryClipboardData(mime, data, len); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::getBinaryClipboardData: %s",e.str()); } } @@ -472,19 +485,14 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, return ret; } -void XserverDesktop::handleClipboardRequest() -{ - vncHandleClipboardRequest(); -} - void XserverDesktop::handleClipboardAnnounce(bool available) { vncHandleClipboardAnnounce(available); } -void XserverDesktop::handleClipboardData(const char* data_, int len) +void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { - vncHandleClipboardData(data_, len); + vncHandleClipboardAnnounceBinary(num, mimes); } void XserverDesktop::grabRegion(const rfb::Region& region) diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 70394fa..fb9b365 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -62,7 +62,11 @@ public: void refreshScreenLayout(); void requestClipboard(); void announceClipboard(bool available); - void sendClipboardData(const char* data); + void clearBinaryClipboardData(); + void sendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len); + void getBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len); void bell(); void setLEDState(unsigned int state); void setDesktopName(const char* name); @@ -94,9 +98,8 @@ public: virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); - virtual void handleClipboardRequest(); virtual void handleClipboardAnnounce(bool available); - virtual void handleClipboardData(const char* data, int len); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); // rfb::PixelBuffer callbacks virtual void grabRegion(const rfb::Region& r); diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index f3a6ac6..a031b3e 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -295,17 +295,22 @@ Allow click releases inside the blacked-out region. . .TP .B \-DLP_ClipSendMax \fIbytes\fP -Limit clipboard bytes to send to clients in one transaction. Default 10,000. +Limit clipboard bytes to send to clients in one transaction. Default 0. 0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely. . .TP .B \-DLP_ClipAcceptMax \fIbytes\fP -Limit clipboard bytes to receive from clients in one transaction. Default 10,000. +Limit clipboard bytes to receive from clients in one transaction. Default 0. 0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely. . .TP .B \-DLP_ClipDelay \fIms\fP -This many milliseconds must pass between clipboard actions. Default 1000. +This many milliseconds must pass between clipboard actions. Default 0, 0 disables the limit. +. +.TP +.B \-DLP_ClipTypes \fIa,b\fP +Allowed binary clipboard mimetypes, separated by commas. Default +chromium/x-web-custom-data,text/html,image/png . .TP .B \-DLP_KeyRateLimit \fIkeys-per-second\fP diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 6b60c0a..66252b9 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -18,10 +18,12 @@ */ #include +#include #include #include #include +#include #include #include @@ -64,6 +66,8 @@ int vncFbstride[MAXSCREENS]; int vncInetdSock = -1; +static std::vector dlp_mimetypes; + struct CaseInsensitiveCompare { bool operator() (const std::string &a, const std::string &b) const { return strcasecmp(a.c_str(), b.c_str()) < 0; @@ -147,6 +151,38 @@ static void parseOverrideList(const char *text, ParamSet &out) } } +static void parseClipTypes() +{ + char *str = strdup(Server::DLP_Clip_Types); + char * const origstr = str; + + while (1) { + char *cur = strsep(&str, ","); + if (!cur) + break; + if (!cur[0]) + continue; + + // Hardcoded filters + if (!strcmp(cur, "TEXT") || + !strcmp(cur, "STRING") || + strstr(cur, "text/plain") || + !strcmp(cur, "UTF8_STRING")) + continue; + + struct dlp_mimetype_t m; + strncpy(m.mime, cur, sizeof(m.mime)); + m.mime[sizeof(m.mime) - 1] = '\0'; + + dlp_mimetypes.push_back(m); + + vlog.debug("Adding DLP binary mime type %s", m.mime); + } + vlog.debug("Total %u binary mime types", dlp_mimetypes.size()); + + free(origstr); +} + void vncExtensionInit(void) { if (vncExtGeneration == vncGetServerGeneration()) { @@ -163,6 +199,8 @@ void vncExtensionInit(void) vncAddExtension(); + if (!initialised) + parseClipTypes(); vncSelectionInit(); vlog.info("VNC extension running!"); @@ -315,22 +353,30 @@ void vncUpdateDesktopName(void) desktop[scr]->setDesktopName(desktopName); } -void vncRequestClipboard(void) +void vncAnnounceClipboard(int available) +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->announceClipboard(available); +} + +void vncSendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->requestClipboard(); + desktop[scr]->sendBinaryClipboardData(mime, data, len); } -void vncAnnounceClipboard(int available) +void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->announceClipboard(available); + desktop[scr]->getBinaryClipboardData(mime, ptr, len); } -void vncSendClipboardData(const char* data) +void vncClearBinaryClipboardData() { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->sendClipboardData(data); + desktop[scr]->clearBinaryClipboardData(); } int vncConnectClient(const char *addr) @@ -486,3 +532,13 @@ int vncOverrideParam(const char *nameAndValue) return rfb::Configuration::setParam(nameAndValue); } + +unsigned dlp_num_mimetypes() +{ + return dlp_mimetypes.size(); +} + +const char *dlp_get_mimetype(const unsigned i) +{ + return dlp_mimetypes[i].mime; +} diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 943537d..0b4abfd 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -45,6 +45,13 @@ int vncNotifyQueryConnect(void); extern void* vncFbptr[]; extern int vncFbstride[]; +struct dlp_mimetype_t { + char mime[32]; +}; + +unsigned dlp_num_mimetypes(); +const char *dlp_get_mimetype(const unsigned i); + extern int vncInetdSock; void vncExtensionInit(void); @@ -60,9 +67,12 @@ int vncGetSendPrimary(void); void vncUpdateDesktopName(void); -void vncRequestClipboard(void); void vncAnnounceClipboard(int available); -void vncSendClipboardData(const char* data); +void vncClearBinaryClipboardData(); +void vncSendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len); +void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len); int vncConnectClient(const char *addr); diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 21a2a61..bdadd70 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -43,6 +43,14 @@ static Atom xaPRIMARY, xaCLIPBOARD; static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING; +static Atom xaINCR; +static Atom *xaBinclips; +static unsigned xaHtmlIndex, xaPngIndex; +static Bool htmlPngPresent; + +static unsigned *mimeIndexesFromClient; +static unsigned numMimesFromClient; +static Bool textFromClient; static WindowPtr pWindow; static Window wid; @@ -66,8 +74,7 @@ static int vncCreateSelectionWindow(void); static int vncOwnSelection(Atom selection); static int vncConvertSelection(ClientPtr client, Atom selection, Atom target, Atom property, - Window requestor, CARD32 time, - const char* data, int len); + Window requestor, CARD32 time); static int vncProcConvertSelection(ClientPtr client); static void vncSelectionRequest(Atom selection, Atom target); static int vncProcSendEvent(ClientPtr client); @@ -90,6 +97,32 @@ void vncSelectionInit(void) xaTEXT = MakeAtom("TEXT", 4, TRUE); xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE); + xaINCR = MakeAtom("INCR", 4, TRUE); + + unsigned i; + mimeIndexesFromClient = calloc(dlp_num_mimetypes(), sizeof(unsigned)); + numMimesFromClient = 0; + textFromClient = FALSE; + xaBinclips = calloc(dlp_num_mimetypes(), sizeof(Atom)); + htmlPngPresent = FALSE; + Bool htmlfound = FALSE, pngfound = FALSE; + for (i = 0; i < dlp_num_mimetypes(); i++) { + const char *cur = dlp_get_mimetype(i); + xaBinclips[i] = MakeAtom(cur, strlen(cur), TRUE); + + if (!strcmp(cur, "text/html")) { + xaHtmlIndex = i; + htmlfound = TRUE; + } + if (!strcmp(cur, "image/png")) { + xaPngIndex = i; + pngfound = TRUE; + } + } + + if (htmlfound && pngfound) + htmlPngPresent = TRUE; + /* There are no hooks for when these are internal windows, so * override the relevant handlers. */ origProcConvertSelection = ProcVector[X_ConvertSelection]; @@ -103,7 +136,7 @@ void vncSelectionInit(void) FatalError("Add VNC ClientStateCallback failed\n"); } -void vncHandleClipboardRequest(void) +static void vncHandleClipboardRequest(void) { if (activeSelection == None) { LOG_DEBUG("Got request for local clipboard although no clipboard is active"); @@ -161,24 +194,55 @@ void vncHandleClipboardAnnounce(int available) } } -void vncHandleClipboardData(const char* data, int len) +void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { - struct VncDataTarget* next; + if (num) { + int rc; - LOG_DEBUG("Got remote clipboard data, sending to X11 clients"); + LOG_DEBUG("Remote binary clipboard announced, grabbing local ownership"); + + if (vncGetSetPrimary()) { + rc = vncOwnSelection(xaPRIMARY); + if (rc != Success) + LOG_ERROR("Could not set PRIMARY selection"); + } + + rc = vncOwnSelection(xaCLIPBOARD); + if (rc != Success) + LOG_ERROR("Could not set CLIPBOARD selection"); + + unsigned i, valid = 0; + for (i = 0; i < num; i++) { + unsigned j; + for (j = 0; j < dlp_num_mimetypes(); j++) { + if (!strcmp(dlp_get_mimetype(j), mimes[i])) { + mimeIndexesFromClient[valid] = j; + valid++; + break; + } + } + + if (!strcmp(mimes[i], "text/plain")) + textFromClient = TRUE; + } + numMimesFromClient = valid; + LOG_DEBUG("Client sent %u mimes, %u were valid", num, valid); + } else { + struct VncDataTarget* next; + numMimesFromClient = 0; + textFromClient = FALSE; + + if (pWindow == NULL) + return; + + LOG_DEBUG("Remote binary clipboard lost, removing local ownership"); + + DeleteWindowFromAnySelections(pWindow); + + /* Abort any pending transfer */ + while (vncDataTargetHead != NULL) { + xEvent event; - while (vncDataTargetHead != NULL) { - int rc; - xEvent event; - - rc = vncConvertSelection(vncDataTargetHead->client, - vncDataTargetHead->selection, - vncDataTargetHead->target, - vncDataTargetHead->property, - vncDataTargetHead->requestor, - vncDataTargetHead->time, - data, len); - if (rc != Success) { event.u.u.type = SelectionNotify; event.u.selectionNotify.time = vncDataTargetHead->time; event.u.selectionNotify.requestor = vncDataTargetHead->requestor; @@ -186,11 +250,11 @@ void vncHandleClipboardData(const char* data, int len) event.u.selectionNotify.target = vncDataTargetHead->target; event.u.selectionNotify.property = None; WriteEventsToClient(vncDataTargetHead->client, 1, &event); - } - next = vncDataTargetHead->next; - free(vncDataTargetHead); - vncDataTargetHead = next; + next = vncDataTargetHead->next; + free(vncDataTargetHead); + vncDataTargetHead = next; + } } } @@ -277,10 +341,22 @@ static int vncOwnSelection(Atom selection) return Success; } +static Bool clientHasBinaryAtom(const Atom target, unsigned *which) +{ + unsigned i; + for (i = 0; i < numMimesFromClient; i++) { + if (xaBinclips[mimeIndexesFromClient[i]] == target) { + *which = mimeIndexesFromClient[i]; + return TRUE; + } + } + + return FALSE; +} + static int vncConvertSelection(ClientPtr client, Atom selection, Atom target, Atom property, - Window requestor, CARD32 time, - const char* data, int len) + Window requestor, CARD32 time) { Selection *pSel; WindowPtr pWin; @@ -290,13 +366,8 @@ static int vncConvertSelection(ClientPtr client, Atom selection, xEvent event; - if (data == NULL) { - LOG_DEBUG("Selection request for %s (type %s)", - NameForAtom(selection), NameForAtom(target)); - } else { - LOG_DEBUG("Sending data for selection request for %s (type %s)", - NameForAtom(selection), NameForAtom(target)); - } + LOG_DEBUG("Selection request for %s (type %s)", + NameForAtom(selection), NameForAtom(target)); rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess); if (rc != Success) @@ -315,14 +386,23 @@ static int vncConvertSelection(ClientPtr client, Atom selection, realProperty = target; /* FIXME: MULTIPLE target */ + unsigned binatomidx; if (target == xaTARGETS) { - Atom targets[] = { xaTARGETS, xaTIMESTAMP, - xaSTRING, xaTEXT, xaUTF8_STRING }; + Atom targets[5 + numMimesFromClient]; + targets[0] = xaTARGETS; + targets[1] = xaTIMESTAMP; + targets[2] = xaSTRING; + targets[3] = xaTEXT; + targets[4] = xaUTF8_STRING; + + unsigned i; + for (i = 0; i < numMimesFromClient; i++) + targets[5 + i] = xaBinclips[mimeIndexesFromClient[i]]; rc = dixChangeWindowProperty(serverClient, pWin, realProperty, XA_ATOM, 32, PropModeReplace, - sizeof(targets)/sizeof(targets[0]), + 5 + numMimesFromClient, targets, TRUE); if (rc != Success) return rc; @@ -333,59 +413,47 @@ static int vncConvertSelection(ClientPtr client, Atom selection, TRUE); if (rc != Success) return rc; - } else { - if (data == NULL) { - struct VncDataTarget* vdt; - - if ((target != xaSTRING) && (target != xaTEXT) && - (target != xaUTF8_STRING)) - return BadMatch; - - vdt = calloc(1, sizeof(struct VncDataTarget)); - if (vdt == NULL) + } else if (clientHasBinaryAtom(target, &binatomidx)) { + const unsigned char *data; + unsigned len; + vncGetBinaryClipboardData(dlp_get_mimetype(binatomidx), &data, &len); + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + xaBinclips[binatomidx], 8, PropModeReplace, + len, (unsigned char *) data, TRUE); + if (rc != Success) + return rc; + } else if (textFromClient) { + const unsigned char *data; + unsigned len; + vncGetBinaryClipboardData("text/plain", &data, &len); + + if ((target == xaSTRING) || (target == xaTEXT)) { + char* latin1; + latin1 = vncUTF8ToLatin1(data, (size_t)-1); + if (latin1 == NULL) return BadAlloc; - vdt->client = client; - vdt->selection = selection; - vdt->target = target; - vdt->property = property; - vdt->requestor = requestor; - vdt->time = time; - - vdt->next = vncDataTargetHead; - vncDataTargetHead = vdt; + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + XA_STRING, 8, PropModeReplace, + len, latin1, TRUE); - LOG_DEBUG("Requesting clipboard data from client"); + vncStrFree(latin1); - vncRequestClipboard(); - - return Success; + if (rc != Success) + return rc; + } else if (target == xaUTF8_STRING) { + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + xaUTF8_STRING, 8, PropModeReplace, + len, (char *) data, TRUE); + if (rc != Success) + return rc; } else { - if ((target == xaSTRING) || (target == xaTEXT)) { - char* latin1; - - latin1 = vncUTF8ToLatin1(data, (size_t)-1); - if (latin1 == NULL) - return BadAlloc; - - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - XA_STRING, 8, PropModeReplace, - len, latin1, TRUE); - - vncStrFree(latin1); - - if (rc != Success) - return rc; - } else if (target == xaUTF8_STRING) { - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - xaUTF8_STRING, 8, PropModeReplace, - len, data, TRUE); - if (rc != Success) - return rc; - } else { - return BadMatch; - } + return BadMatch; } + } else { + LOG_ERROR("Text clipboard paste requested, but client sent no text"); + + return BadMatch; } event.u.u.type = SelectionNotify; @@ -424,7 +492,7 @@ static int vncProcConvertSelection(ClientPtr client) pSel->window == wid) { rc = vncConvertSelection(client, stuff->selection, stuff->target, stuff->property, - stuff->requestor, stuff->time, NULL, 0); + stuff->requestor, stuff->time); if (rc != Success) { xEvent event; @@ -483,6 +551,21 @@ static Bool vncHasAtom(Atom atom, const Atom list[], size_t size) return FALSE; } +static Bool vncHasBinaryClipboardAtom(const Atom list[], size_t size) +{ + size_t i, b; + const unsigned num = dlp_num_mimetypes(); + + for (i = 0;i < size;i++) { + for (b = 0; b < num; b++) { + if (list[i] == xaBinclips[b]) + return TRUE; + } + } + + return FALSE; +} + static void vncHandleSelection(Atom selection, Atom target, Atom property, Atom requestor, TimeStamp time) @@ -502,6 +585,9 @@ static void vncHandleSelection(Atom selection, Atom target, if (target != property) return; + if (prop->type == xaINCR) + LOG_INFO("Incremental clipboard transfer denied, too large"); + if (target == xaTARGETS) { if (prop->format != 32) return; @@ -510,16 +596,36 @@ static void vncHandleSelection(Atom selection, Atom target, if (probing) { if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) || - vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) { + vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size) || + vncHasBinaryClipboardAtom((const Atom*)prop->data, prop->size)) { LOG_DEBUG("Compatible format found, notifying clients"); + vncClearBinaryClipboardData(); activeSelection = selection; vncAnnounceClipboard(TRUE); + vncHandleClipboardRequest(); } } else { if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaUTF8_STRING); else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaSTRING); + + unsigned i; + + Bool skiphtml = FALSE; + if (htmlPngPresent && + vncHasAtom(xaBinclips[xaHtmlIndex], (const Atom*)prop->data, prop->size) && + vncHasAtom(xaBinclips[xaPngIndex], (const Atom*)prop->data, prop->size)) + skiphtml = TRUE; + + for (i = 0; i < dlp_num_mimetypes(); i++) { + if (skiphtml && i == xaHtmlIndex) + continue; + if (vncHasAtom(xaBinclips[i], (const Atom*)prop->data, prop->size)) { + vncSelectionRequest(selection, xaBinclips[i]); + //break; + } + } } } else if (target == xaSTRING) { char* filtered; @@ -539,10 +645,10 @@ static void vncHandleSelection(Atom selection, Atom target, if (utf8 == NULL) return; - LOG_DEBUG("Sending clipboard to clients (%d bytes)", + LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)", (int)strlen(utf8)); - vncSendClipboardData(utf8); + vncSendBinaryClipboardData("text/plain", utf8, strlen(utf8)); vncStrFree(utf8); } else if (target == xaUTF8_STRING) { @@ -557,12 +663,31 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - LOG_DEBUG("Sending clipboard to clients (%d bytes)", + LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)", (int)strlen(filtered)); - vncSendClipboardData(filtered); + vncSendBinaryClipboardData("text/plain", filtered, strlen(filtered)); vncStrFree(filtered); + } else { + unsigned i; + + if (prop->format != 8) + return; + + for (i = 0; i < dlp_num_mimetypes(); i++) { + if (target == xaBinclips[i]) { + if (prop->type != xaBinclips[i]) + return; + + LOG_DEBUG("Sending binary clipboard to clients (%d bytes)", + prop->size); + + vncSendBinaryClipboardData(dlp_get_mimetype(i), prop->data, prop->size); + + break; + } + } } } diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h index 337f9bf..68266bf 100644 --- a/unix/xserver/hw/vnc/vncSelection.h +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -24,9 +24,8 @@ extern "C" { void vncSelectionInit(void); -void vncHandleClipboardRequest(void); void vncHandleClipboardAnnounce(int available); -void vncHandleClipboardData(const char* data, int len); +void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); #ifdef __cplusplus } From d3add8150cdf329776ae79a4c0b4ebec21c75cc6 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Mon, 18 Oct 2021 07:38:57 -0400 Subject: [PATCH 15/22] Bugfix/kasm 2004 download spaces (#70) * Handle spaces in download files, dirlisting * Disallow / in percent urls Co-authored-by: Lauri Kasanen --- common/network/websocket.c | 54 +++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/common/network/websocket.c b/common/network/websocket.c index e9ad04a..6270b49 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -111,7 +111,7 @@ static const char *parse_get(const char * const in, const char * const opt, unsi return ""; } -static void percent_decode(const char *src, char *dst) { +static void percent_decode(const char *src, char *dst, const uint8_t filter) { while (1) { if (!*src) break; @@ -127,7 +127,32 @@ static void percent_decode(const char *src, char *dst) { hex[2] = '\0'; src += 2; - *dst++ = strtol(hex, NULL, 16); + *dst = strtol(hex, NULL, 16); + + if (filter) { + // Avoid directory traversal + if (*dst == '/') + *dst = '_'; + } + + dst++; + } + } + + *dst = '\0'; +} + +static void percent_encode(const char *src, char *dst) { + while (1) { + if (!*src) + break; + if (isalnum(*src) || *src == '.' || *src == ',') { + *dst++ = *src++; + } else { + *dst++ = '%'; + sprintf(dst, "%02X", *src); + dst += 2; + src++; } } @@ -746,6 +771,7 @@ def: static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) { char buf[4096]; + char enc[PATH_MAX * 3 + 1]; unsigned i; // Redirect? @@ -779,11 +805,13 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[ if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, "..")) continue; + percent_encode(names[i]->d_name, enc); + if (names[i]->d_type == DT_DIR) - sprintf(buf, "
  • %s/
  • ", names[i]->d_name, + sprintf(buf, "
  • %s/
  • ", enc, names[i]->d_name); else - sprintf(buf, "
  • %s
  • ", names[i]->d_name, + sprintf(buf, "
  • %s
  • ", enc, names[i]->d_name); ws_send(ws_ctx, buf, strlen(buf)); @@ -822,13 +850,15 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) { len = strlen(path); } - wserr("Requested file '%s'\n", path); - sprintf(fullpath, "%s/%s", settings.httpdir, path); + percent_decode(path, buf, 1); + + wserr("Requested file '%s'\n", buf); + sprintf(fullpath, "%s/%s", settings.httpdir, buf); DIR *dir = opendir(fullpath); if (dir) { closedir(dir); - dirlisting(ws_ctx, fullpath, path); + dirlisting(ws_ctx, fullpath, buf); return; } @@ -976,14 +1006,14 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } param = parse_get(args, "password", &len); if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decpw); + percent_decode(buf, decpw, 0); struct crypt_data cdata; cdata.initialized = 0; @@ -1023,7 +1053,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1052,7 +1082,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1097,7 +1127,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } else { wserr("client param required\n"); goto nope; From 6219b85ba815930cde2b66839a75f46d60408440 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Tue, 19 Oct 2021 15:44:36 -0400 Subject: [PATCH 16/22] Feature/kasm 786 arm support (#71) * KASM-786 ARM Support, with CI pipelines for debian and ubuntu supported operating systems --- .gitlab-ci.yml | 95 ++++++++++++++++++- builder/README.md | 30 ++++++ builder/build.sh | 15 ++- builder/dockerfile.centos_core.build | 2 +- builder/dockerfile.debian_bullseye.build | 2 +- builder/dockerfile.debian_buster.build | 2 +- builder/dockerfile.fedora_thirtythree.build | 2 +- builder/dockerfile.kali_kali-rolling.build | 2 +- ...e.ubuntu_bionic+libjpeg-turbo_latest.build | 2 +- builder/dockerfile.ubuntu_bionic.build | 2 +- builder/dockerfile.ubuntu_focal.build | 2 +- debian/control | 4 +- 12 files changed, 143 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 771a4ae..ed6a950 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,25 +8,61 @@ variables: GIT_FETCH_EXTRA_FLAGS: --tags stages: + - www - build - upload .prepare_build: &prepare_build - - ls -l - pwd - apk add bash - mkdir -p "$GITLAB_SHARED_DIND_DIR" && chmod 777 "$GITLAB_SHARED_DIND_DIR" +.prepare_www: &prepare_www + - tar -zxf output/www/kasm_www.tar.gz -C builder/ + .prepare_artfacts: &prepare_artfacts - - mkdir output - cp -r builder/build/* output/ - rm output/*.tar.gz +build_www: + stage: www + allow_failure: false + before_script: + - *prepare_build + script: + - webpacked_www=$PWD/builder/www + - src_www=kasmweb + - docker build -t kasmweb/www -f builder/dockerfile.www.build . + - docker run --rm -v $PWD/builder/www:/build kasmweb/www:latest + - mkdir -p output/www + - cd builder + - tar -zcvf ../output/www/kasm_www.tar.gz www + artifacts: + paths: + - output/ + build_ubuntu_bionic: stage: build allow_failure: true before_script: - *prepare_build + - *prepare_www + after_script: + - *prepare_artfacts + script: + - bash builder/build-package ubuntu bionic + artifacts: + paths: + - output/ + +build_ubuntu_bionic_arm: + stage: build + allow_failure: false + tags: + - arm + before_script: + - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -40,6 +76,7 @@ build_ubuntu_bionic_libjpeg_turbo: allow_failure: false before_script: - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -53,6 +90,23 @@ build_ubuntu_focal: allow_failure: true before_script: - *prepare_build + - *prepare_www + after_script: + - *prepare_artfacts + script: + - bash builder/build-package ubuntu focal; + artifacts: + paths: + - output/ + +build_ubuntu_focal_arm: + stage: build + allow_failure: true + tags: + - arm + before_script: + - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -66,6 +120,23 @@ build_debian_buster: allow_failure: true before_script: - *prepare_build + - *prepare_www + after_script: + - *prepare_artfacts + script: + - bash builder/build-package debian buster; + artifacts: + paths: + - output/ + +build_debian_buster_arm: + stage: build + allow_failure: true + tags: + - arm + before_script: + - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -79,6 +150,23 @@ build_debian_bullseye: allow_failure: true before_script: - *prepare_build + - *prepare_www + after_script: + - *prepare_artfacts + script: + - bash builder/build-package debian bullseye; + artifacts: + paths: + - output/ + +build_debian_bullseye_arm: + stage: build + allow_failure: true + tags: + - arm + before_script: + - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -92,6 +180,7 @@ build_kali_rolling: allow_failure: true before_script: - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -105,6 +194,7 @@ build_centos7: allow_failure: true before_script: - *prepare_build + - *prepare_www after_script: - *prepare_artfacts script: @@ -127,4 +217,5 @@ upload: echo; echo "File to upload $upload_filename"; upload_to_s3 "$package" "$upload_filename"; + curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=$upload_filename&target_url=${S3_URL}"; done diff --git a/builder/README.md b/builder/README.md index acae94e..9d59373 100644 --- a/builder/README.md +++ b/builder/README.md @@ -90,3 +90,33 @@ bash -c ' prepare_upload_filename "bionic/kasmvncserver_0.9.1~beta-1+libjpeg-turbo-latest_amd64.deb"; echo $upload_filename;' ``` + +# ARM + +KasmVNC is supported on ARM, however, the build process needs to be broken into two parts with one occuring on a x64 system and the other on an ARM system. All our testing and official builds are done on AWS Graviton instances. + +### Build www code on x86 System +The www code is webpacked for performance and thus requires building. There are NPM packages, phantomjs, which do not have an ARM build. Therefore, this must be built on x86 and then copied over to the ARM system for final packaging. + +``` +cd ~/KasmVNC +mkdir builder/www +sudo docker build -t kasmweb/www -f builder/dockerfile.www.build . +sudo docker run --rm -v $PWD/builder/www:/build kasmweb/www:latest +cd builder +tar -zcvf /tmp/kasm_www.tar.gz www +``` + +Now transfer ```kasm_www.tar.gz``` to the ARM system. + +### Build KasmVNC ARM +These instructions assume KasmVNC has been cloned at $HOME and ```kasm_www.tar.gz``` has been placed at $HOME as well, adjust for your environment. + +``` +cd ~ +tar -zxf kasm_www.tar.gz -C KasmVNC/builder/ +cd KasmVNC +sudo builder/build-package ubuntu bionic +``` +The resulting deb package can be found under ~/KasmVNC/builder/build/bionic +Replace ```bionic``` with ```focal``` to build for Ubuntu 20.04LTS. At this time, only Ubuntu Bionic has been tested, however, other Debian based builds we support should also work. diff --git a/builder/build.sh b/builder/build.sh index 4192f36..9db927c 100755 --- a/builder/build.sh +++ b/builder/build.sh @@ -30,16 +30,19 @@ sed -i -e '/find_package(FLTK/s@^@#@' \ -e '/add_subdirectory(tests/s@^@#@' \ CMakeLists.txt -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo . +cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo . -DBUILD_VIEWER:BOOL=OFF make -j5 -tar -C unix/xserver -xvf /tmp/xorg-server-${XORG_VER}.tar.bz2 --strip-components=1 +tar -C unix/xserver -xf /tmp/xorg-server-${XORG_VER}.tar.bz2 --strip-components=1 cd unix/xserver patch -Np1 -i ../xserver${XORG_PATCH}.patch -if [[ $XORG_VER =~ ^1\.20\..*$ ]]; then - patch -Np1 -i ../xserver120.7.patch -fi +case "$XORG_VER" in + 1.20.*) + if [ -f ../xserver120.7.patch ]; then + patch -Np1 -i ../xserver120.7.patch + fi ;; +esac autoreconf -i # Configuring Xorg is long and has many distro-specific paths. @@ -72,6 +75,8 @@ mkdir lib cd lib if [ -d /usr/lib/x86_64-linux-gnu/dri ]; then ln -s /usr/lib/x86_64-linux-gnu/dri dri +elif [ -d /usr/lib/aarch64-linux-gnu/dri ]; then + ln -s /usr/lib/aarch64-linux-gnu/dri dri else ln -s /usr/lib64/dri dri fi diff --git a/builder/dockerfile.centos_core.build b/builder/dockerfile.centos_core.build index c277e1a..a94ee76 100644 --- a/builder/dockerfile.centos_core.build +++ b/builder/dockerfile.centos_core.build @@ -16,7 +16,7 @@ RUN yum install -y mesa-dri-drivers # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.debian_bullseye.build b/builder/dockerfile.debian_bullseye.build index 0a8428e..707facd 100644 --- a/builder/dockerfile.debian_bullseye.build +++ b/builder/dockerfile.debian_bullseye.build @@ -17,7 +17,7 @@ RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libg # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.debian_buster.build b/builder/dockerfile.debian_buster.build index a47b6fe..569cc2f 100644 --- a/builder/dockerfile.debian_buster.build +++ b/builder/dockerfile.debian_buster.build @@ -17,7 +17,7 @@ RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libg # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.fedora_thirtythree.build b/builder/dockerfile.fedora_thirtythree.build index 8dc4b29..537ab6b 100644 --- a/builder/dockerfile.fedora_thirtythree.build +++ b/builder/dockerfile.fedora_thirtythree.build @@ -24,7 +24,7 @@ RUN dnf install -y bzip2 redhat-lsb-core # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.kali_kali-rolling.build b/builder/dockerfile.kali_kali-rolling.build index 9705e42..05e5ebb 100644 --- a/builder/dockerfile.kali_kali-rolling.build +++ b/builder/dockerfile.kali_kali-rolling.build @@ -17,7 +17,7 @@ RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libg # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build b/builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build index 7f6ca1d..12482e6 100644 --- a/builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build +++ b/builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build @@ -20,7 +20,7 @@ RUN export MAKEFLAGS=-j`nproc`; cd libjpeg-turbo && cmake -DCMAKE_INSTALL_PREFIX # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.ubuntu_bionic.build b/builder/dockerfile.ubuntu_bionic.build index d95bd02..5584fe2 100644 --- a/builder/dockerfile.ubuntu_bionic.build +++ b/builder/dockerfile.ubuntu_bionic.build @@ -15,7 +15,7 @@ RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libg # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/builder/dockerfile.ubuntu_focal.build b/builder/dockerfile.ubuntu_focal.build index d0562d1..fb3f800 100644 --- a/builder/dockerfile.ubuntu_focal.build +++ b/builder/dockerfile.ubuntu_focal.build @@ -17,7 +17,7 @@ RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libg # Additions for webp RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz -RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp && tar -xzf /tmp/libwebp-* RUN cd /tmp/libwebp-1.0.2 && \ ./configure --enable-static --disable-shared && \ make && make install diff --git a/debian/control b/debian/control index 623d913..7ead94c 100644 --- a/debian/control +++ b/debian/control @@ -3,14 +3,14 @@ Section: x11 Priority: optional Maintainer: Kasm Technologies LLC Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev, - libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6 + libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6, libunwind8 Standards-Version: 4.1.3 Homepage: https://github.com/kasmtech/KasmVNC #Vcs-Browser: https://salsa.debian.org/debian/kasmvnc #Vcs-Git: https://salsa.debian.org/debian/kasmvnc.git Package: kasmvncserver -Architecture: amd64 +Architecture: amd64 arm64 Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth, x11-xkb-utils, xkb-data, procps Provides: vnc-server From a9434a300dbf85e65d0e9c212610a487fd10a308 Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Wed, 20 Oct 2021 15:24:22 -0400 Subject: [PATCH 17/22] Feature/kasm 2029 pipeline tweaks (#72) * update to Gitlab CI pipeline builds --- .gitlab-ci.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed6a950..f5d1490 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -95,6 +95,9 @@ build_ubuntu_focal: - *prepare_artfacts script: - bash builder/build-package ubuntu focal; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [Cc][Ii]\]/ artifacts: paths: - output/ @@ -111,6 +114,9 @@ build_ubuntu_focal_arm: - *prepare_artfacts script: - bash builder/build-package ubuntu focal; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [Cc][Ii]\]/ artifacts: paths: - output/ @@ -125,6 +131,9 @@ build_debian_buster: - *prepare_artfacts script: - bash builder/build-package debian buster; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/ artifacts: paths: - output/ @@ -141,6 +150,9 @@ build_debian_buster_arm: - *prepare_artfacts script: - bash builder/build-package debian buster; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/ artifacts: paths: - output/ @@ -155,6 +167,9 @@ build_debian_bullseye: - *prepare_artfacts script: - bash builder/build-package debian bullseye; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/ artifacts: paths: - output/ @@ -171,6 +186,9 @@ build_debian_bullseye_arm: - *prepare_artfacts script: - bash builder/build-package debian bullseye; + only: + variables: + - $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/ artifacts: paths: - output/ @@ -217,5 +235,6 @@ upload: echo; echo "File to upload $upload_filename"; upload_to_s3 "$package" "$upload_filename"; - curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=$upload_filename&target_url=${S3_URL}"; + UPLOAD_NAME=$(echo $upload_filename | sed 's#kasmvncserver_##' | sed -r 's#_([0-9]{1,3}\.){2}[0-9]{1,2}_\S+?([a-f0-9]{6})##' | sed -r 's#\.(deb|rpm)##'); + curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=${UPLOAD_NAME}&target_url=${S3_URL}"; done From dd276e5e879bf3aff2c4177388f91eb15dd905ae Mon Sep 17 00:00:00 2001 From: mmcclaskey Date: Mon, 25 Oct 2021 14:40:54 -0400 Subject: [PATCH 18/22] Default sendprimary to off (#73) Co-authored-by: Lauri Kasanen --- unix/xserver/hw/vnc/Xvnc.man | 2 +- unix/xserver/hw/vnc/vncExtInit.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index a031b3e..7cafa02 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -137,7 +137,7 @@ Send clipboard changes to clients. Default is on. .TP .B \-SendPrimary Send the primary selection and cut buffer to the server as well as the -clipboard selection. Default is on. +clipboard selection. Default is off. . .TP .B \-AcceptPointerEvents diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 66252b9..503b2b3 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -107,7 +107,7 @@ rfb::BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well " "as the CLIPBOARD selection", true); rfb::BoolParameter sendPrimary("SendPrimary", "Send the PRIMARY as well as the CLIPBOARD selection", - true); + false); static PixelFormat vncGetPixelFormat(int scrIdx) { From 77b97415efe270c53733d32a53cb11b8c0375433 Mon Sep 17 00:00:00 2001 From: Dmitry Maksyoma Date: Sat, 6 Nov 2021 03:05:13 +1300 Subject: [PATCH 19/22] Add bump-package-version(1) and document it (#74) --- builder/README.md | 19 +++++++++ builder/bump-package-version | 41 +++++++++++++++++++ .../bump-package-version-inside-docker-deb | 15 +++++++ builder/dockerfile.bump-package-version | 6 +++ 4 files changed, 81 insertions(+) create mode 100755 builder/bump-package-version create mode 100755 builder/bump-package-version-inside-docker-deb create mode 100644 builder/dockerfile.bump-package-version diff --git a/builder/README.md b/builder/README.md index 9d59373..f840c78 100644 --- a/builder/README.md +++ b/builder/README.md @@ -79,6 +79,25 @@ packages installed with XFCE. ``` builder/test-deb-barebones ubuntu focal ``` + +# Preparing a release + +Deb and rpm packages need their versions bumped to the new release version. It +can be done with: + +``` +builder/bump-package-version 0.9.4-beta +``` + +This will update corresponding package files, use `git diff` to see changes. + +If you've ran the command and curious about Debian version specifics, here's an +explanation: +Deb version will be `0.9.4~beta-1`. `~` (and not `-`) is required by packaging +guidelines, and `-1` is Debian package revision for `0.9.4` upstream release. If +a Debian-specific patch was later added on top of `0.9.4`, it'd be `-2` for the +next Debian version. Rpm has a corresponding revision in its .spec file. + # CI development S3 upload code is extracted to various files in `.ci`. It's possible to iterate diff --git a/builder/bump-package-version b/builder/bump-package-version new file mode 100755 index 0000000..7c2478f --- /dev/null +++ b/builder/bump-package-version @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -eo pipefail + +update_version_to_meet_packaging_standards() { + new_version=$(echo "$new_version" | + sed -e 's/\([0-9]\)-\([a-zA-Z]\)/\1~\2/') +} + +add_debian_revision_to_new_version() { + echo "$new_version-1" +} + +bump_rpm() { + sed -i "s/^Version:.\+/Version: $new_version/" centos/kasmvncserver.spec +} + +bump_deb() { + local image="debbump_package_version:dev" + local L_UID=$(id -u) + local L_GID=$(id -g) + local debian_version=$(add_debian_revision_to_new_version) + + docker build -t "$image" -f builder/dockerfile.bump-package-version . + docker run --rm -v "$PWD":/src --user "$L_UID:$L_GID" \ + "$image" /bin/bash -c \ + "cd /src && builder/bump-package-version-inside-docker-deb $debian_version" +} + +new_version="$1" + +if [[ -z "$new_version" ]]; then + echo >&2 "Usage: $(basename "$0") " + exit 1 +fi + +cd "$(dirname "$0")/.." + +update_version_to_meet_packaging_standards +bump_rpm +bump_deb diff --git a/builder/bump-package-version-inside-docker-deb b/builder/bump-package-version-inside-docker-deb new file mode 100755 index 0000000..ffdbbe3 --- /dev/null +++ b/builder/bump-package-version-inside-docker-deb @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + +new_version="$1" + +update_version() { + dch --newversion $new_version 'New upstream release.' +} + +mark_as_released() { + dch --release "" +} + +update_version +mark_as_released diff --git a/builder/dockerfile.bump-package-version b/builder/dockerfile.bump-package-version new file mode 100644 index 0000000..89bb570 --- /dev/null +++ b/builder/dockerfile.bump-package-version @@ -0,0 +1,6 @@ +FROM debian:buster + +ENV DEBEMAIL="Kasm Technologies LLC " + +RUN apt-get update && \ + apt-get -y install vim devscripts From 314e7e961500bb0a3c8ee813a3177f3f1d78ac07 Mon Sep 17 00:00:00 2001 From: Mariusz Marciniak Date: Tue, 19 Oct 2021 20:45:19 +0000 Subject: [PATCH 20/22] KASM-2001 Update noVNC commit --- kasmweb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb b/kasmweb index 0bd3813..b5a1586 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit 0bd38139494ef1bbaf580c2f4892c7be3a291b19 +Subproject commit b5a1586c0aa7731cb2dd772e90b83775c36a1256 From 26f1b52b70dede7bce2f5fe1b1e9c14d2e1069d8 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 9 Nov 2021 19:04:07 +0000 Subject: [PATCH 21/22] KASM-2075 update novnc to head of master --- kasmweb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb b/kasmweb index b5a1586..170cb81 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit b5a1586c0aa7731cb2dd772e90b83775c36a1256 +Subproject commit 170cb81f6f2a864000e77f585d037e5c11c8554c From b8424607244cd3d5cf4ae456c4d3f6344cbd94f9 Mon Sep 17 00:00:00 2001 From: Mariusz Marciniak Date: Mon, 15 Nov 2021 16:32:13 +0100 Subject: [PATCH 22/22] KASM-2108 Update noVNC commit --- kasmweb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kasmweb b/kasmweb index 170cb81..9383783 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit 170cb81f6f2a864000e77f585d037e5c11c8554c +Subproject commit 9383783efd57245b91114cea85cf8cdab3433e0f