Merge branch 'feature/KASM-4489_watermark_time' into 'master'

Resolve KASM-4489 "Feature/ watermark time"

Closes KASM-4489

See merge request kasm-technologies/internal/KasmVNC!103
This commit is contained in:
Matthew McClaskey 2023-07-26 23:55:06 +00:00
commit b9e9a2d2a0
24 changed files with 3531 additions and 13 deletions

View File

@ -154,6 +154,9 @@ find_package(PNG REQUIRED)
# Check for libjpeg # Check for libjpeg
find_package(JPEG REQUIRED) find_package(JPEG REQUIRED)
find_package(Freetype REQUIRED)
include_directories(${FREETYPE_INCLUDE_DIRS})
# Staticly link libjpeg-turbo # Staticly link libjpeg-turbo
set(JPEG_LIBRARIES "-Wl,-Bstatic -lturbojpeg -Wl,-Bdynamic") set(JPEG_LIBRARIES "-Wl,-Bstatic -lturbojpeg -Wl,-Bdynamic")
# Warn if it doesn't seem to be the accelerated libjpeg that's found # Warn if it doesn't seem to be the accelerated libjpeg that's found

View File

@ -58,7 +58,8 @@ RUN echo 'alias tv="./run-specs spec/vncserver_yaml_validation_spec.py"' >> ~/.b
RUN echo 'alias ty="./run-specs spec/vncserver_*spec.py"' >> ~/.bashrc RUN echo 'alias ty="./run-specs spec/vncserver_*spec.py"' >> ~/.bashrc
RUN echo 'alias ta="./run-specs"' >> ~/.bashrc RUN echo 'alias ta="./run-specs"' >> ~/.bashrc
RUN echo 'alias vd="vncserver -dry-run"' >> ~/.bashrc RUN echo 'alias vd="vncserver -dry-run"' >> ~/.bashrc
ENV SET_PASSWORD_FUNC 'sp() { echo -e "$VNC_PW\\n$VNC_PW\\n" | kasmvncpasswd -w -u $USER $HOME/.kasmpasswd; }' RUN echo 'alias ss="sp; vncserver -select-de xfce"' >> ~/.bashrc
ENV SET_PASSWORD_FUNC 'sp() { echo -e "$VNC_PW\\n$VNC_PW\\n" | kasmvncpasswd -w -u $VNC_USER $HOME/.kasmpasswd; }'
RUN echo $SET_PASSWORD_FUNC >> ~/.bashrc RUN echo $SET_PASSWORD_FUNC >> ~/.bashrc
ENV LC_ALL=C.UTF-8 ENV LC_ALL=C.UTF-8

View File

@ -7,7 +7,7 @@ License: GPLv2+
URL: https://github.com/kasmtech/KasmVNC URL: https://github.com/kasmtech/KasmVNC
BuildRequires: rsync BuildRequires: rsync
Requires: xorg-x11-xauth, xorg-x11-xkb-utils, xkeyboard-config, xorg-x11-server-utils, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny Requires: xorg-x11-xauth, xorg-x11-xkb-utils, xkeyboard-config, xorg-x11-server-utils, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, perl-DateTime-TimeZone
Conflicts: tigervnc-server, tigervnc-server-minimal Conflicts: tigervnc-server, tigervnc-server-minimal
%description %description

View File

@ -0,0 +1,17 @@
# - Find freetype
# Find the freetype libraries
#
# This module defines the following variables:
# FREETYPE_FOUND - True if freetype is found
# FREETYPE_INCLUDE_DIRS - include directories
#
find_package(PkgConfig)
pkg_check_modules(PC_FREETYPE freetype2)
find_path(FREETYPE_INCLUDE_DIRS NAMES ft2build.h HINTS ${PC_FREETYPE_INCLUDE_DIRS})
include(FindPackageHandleStandardArgs)
set(FPHSA_NAME_MISMATCHED 1)
find_package_handle_standard_args(freetype DEFAULT_MSG FREETYPE_INCLUDE_DIRS)
unset(FPHSA_NAME_MISMATCHED)
mark_as_advanced(FREETYPE_INCLUDE_DIRS)

View File

@ -189,6 +189,18 @@ rfb::IntParameter rfb::Server::DLP_WatermarkRepeatSpace
("DLP_WatermarkRepeatSpace", ("DLP_WatermarkRepeatSpace",
"Number of pixels between repeats of the watermark", "Number of pixels between repeats of the watermark",
0, 0, 4096); 0, 0, 4096);
rfb::IntParameter rfb::Server::DLP_WatermarkFontSize
("DLP_WatermarkFontSize",
"Font size for -DLP_WatermarkText",
48, 8, 256);
rfb::IntParameter rfb::Server::DLP_WatermarkTimeOffset
("DLP_WatermarkTimeOffset",
"Offset from UTC for -DLP_WatermarkText",
0, -24, 24);
rfb::IntParameter rfb::Server::DLP_WatermarkTimeOffsetMinutes
("DLP_WatermarkTimeOffsetMinutes",
"Offset from UTC for -DLP_WatermarkText, minutes",
0, -24 * 60, 24 * 60);
rfb::StringParameter rfb::Server::DLP_WatermarkImage rfb::StringParameter rfb::Server::DLP_WatermarkImage
("DLP_WatermarkImage", ("DLP_WatermarkImage",
"PNG file to use as a watermark", "PNG file to use as a watermark",
@ -201,6 +213,14 @@ rfb::StringParameter rfb::Server::DLP_WatermarkTint
("DLP_WatermarkTint", ("DLP_WatermarkTint",
"Tint the greyscale watermark by this color.", "Tint the greyscale watermark by this color.",
"255,255,255,255"); "255,255,255,255");
rfb::StringParameter rfb::Server::DLP_WatermarkText
("DLP_WatermarkText",
"Use this text instead of an image for the watermark, with strftime time formatting",
"");
rfb::StringParameter rfb::Server::DLP_WatermarkFont
("DLP_WatermarkFont",
"Use this font for -DLP_WatermarkText instead of the bundled one",
"");
rfb::StringParameter rfb::Server::maxVideoResolution rfb::StringParameter rfb::Server::maxVideoResolution
("MaxVideoResolution", ("MaxVideoResolution",

View File

@ -49,12 +49,17 @@ namespace rfb {
static IntParameter DLP_ClipDelay; static IntParameter DLP_ClipDelay;
static IntParameter DLP_KeyRateLimit; static IntParameter DLP_KeyRateLimit;
static IntParameter DLP_WatermarkRepeatSpace; static IntParameter DLP_WatermarkRepeatSpace;
static IntParameter DLP_WatermarkFontSize;
static IntParameter DLP_WatermarkTimeOffset;
static IntParameter DLP_WatermarkTimeOffsetMinutes;
static StringParameter DLP_ClipLog; static StringParameter DLP_ClipLog;
static StringParameter DLP_Region; static StringParameter DLP_Region;
static StringParameter DLP_Clip_Types; static StringParameter DLP_Clip_Types;
static StringParameter DLP_WatermarkImage; static StringParameter DLP_WatermarkImage;
static StringParameter DLP_WatermarkLocation; static StringParameter DLP_WatermarkLocation;
static StringParameter DLP_WatermarkTint; static StringParameter DLP_WatermarkTint;
static StringParameter DLP_WatermarkText;
static StringParameter DLP_WatermarkFont;
static BoolParameter DLP_RegionAllowClick; static BoolParameter DLP_RegionAllowClick;
static BoolParameter DLP_RegionAllowRelease; static BoolParameter DLP_RegionAllowRelease;
static IntParameter jpegVideoQuality; static IntParameter jpegVideoQuality;

View File

@ -974,6 +974,11 @@ void VNCServerST::writeUpdate()
blackOut(); blackOut();
} }
if (watermarkData && Server::DLP_WatermarkText[0] && watermarkTextNeedsUpdate(true)) {
// If using a text watermark, we have to mark everything as changed...
refreshClients();
}
comparer->getUpdateInfo(&ui, pb->getRect()); comparer->getUpdateInfo(&ui, pb->getRect());
toCheck = ui.changed.union_(ui.copied); toCheck = ui.changed.union_(ui.copied);

View File

@ -20,10 +20,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <zlib.h> #include <zlib.h>
#include <rfb/LogWriter.h> #include <rfb/LogWriter.h>
#include <rfb/ServerCore.h> #include <rfb/ServerCore.h>
#include <rfb/VNCServerST.h> #include <rfb/VNCServerST.h>
#include "font.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include "Watermark.h" #include "Watermark.h"
@ -36,6 +40,10 @@ watermarkInfo_t watermarkInfo;
uint8_t *watermarkData, *watermarkUnpacked, *watermarkTmp; uint8_t *watermarkData, *watermarkUnpacked, *watermarkTmp;
uint32_t watermarkDataLen; uint32_t watermarkDataLen;
static uint16_t rw, rh; static uint16_t rw, rh;
static time_t lastUpdate;
static FT_Library ft = NULL;
static FT_Face face;
#define MAXW 4096 #define MAXW 4096
#define MAXH 4096 #define MAXH 4096
@ -92,15 +100,151 @@ static bool loadimage(const char path[]) {
return true; return true;
} }
// Note: w and h are absolute
static void str(uint8_t *buf, const char *txt, const uint32_t x_, const uint32_t y_,
const uint32_t w, const uint32_t h,
const uint32_t stride) {
unsigned ucs[256], i, ucslen;
unsigned len = strlen(txt);
i = 0;
ucslen = 0;
while (len > 0 && txt[i]) {
size_t ret = rfb::utf8ToUCS4(&txt[i], len, &ucs[ucslen]);
i += ret;
len -= ret;
ucslen++;
}
uint32_t x, y;
x = x_;
y = y_;
for (i = 0; i < ucslen; i++) {
if (FT_Load_Char(face, ucs[i], FT_LOAD_RENDER))
continue;
const FT_Bitmap * const map = &(face->glyph->bitmap);
if (FT_HAS_KERNING(face) && i) {
FT_Vector delta;
FT_Get_Kerning(face, ucs[i - 1], ucs[i], ft_kerning_default, &delta);
x += delta.x >> 6;
}
uint32_t row, col;
for (row = 0; row < (uint32_t) map->rows; row++) {
int ny = row + y - face->glyph->bitmap_top;
if (ny < 0)
continue;
if ((unsigned) ny >= h)
continue;
uint8_t *dst = (uint8_t *) buf;
dst += ny * stride + x;
const uint8_t *src = map->buffer + map->pitch * row;
for (col = 0; col < (uint32_t) map->width; col++) {
if (col + x >= w)
continue;
const uint8_t out = (src[col] + 8) >> 4;
dst[col] = out < 16 ? out : 15;
}
}
x += face->glyph->advance.x >> 6;
}
}
static uint32_t drawnwidth(const char *txt) {
unsigned ucs[256], i, ucslen;
unsigned len = strlen(txt);
i = 0;
ucslen = 0;
while (len > 0 && txt[i]) {
size_t ret = rfb::utf8ToUCS4(&txt[i], len, &ucs[ucslen]);
i += ret;
len -= ret;
ucslen++;
}
uint32_t x;
x = 0;
for (i = 0; i < ucslen; i++) {
if (FT_Load_Char(face, ucs[i], FT_LOAD_DEFAULT))
continue;
if (FT_HAS_KERNING(face) && i) {
FT_Vector delta;
FT_Get_Kerning(face, ucs[i - 1], ucs[i], ft_kerning_default, &delta);
x += delta.x >> 6;
}
x += face->glyph->advance.x >> 6;
}
return x;
}
static bool drawtext(const char fmt[], const int16_t utcOff, const char fontpath[],
const uint8_t fontsize) {
char buf[PATH_MAX];
if (!ft) {
if (FT_Init_FreeType(&ft))
abort();
if (fontpath[0]) {
if (FT_New_Face(ft, fontpath, 0, &face))
abort();
} else {
if (FT_New_Memory_Face(ft, font_otf, sizeof(font_otf), 0, &face))
abort();
}
FT_Set_Pixel_Sizes(face, fontsize, fontsize);
}
time_t now = lastUpdate = time(NULL);
now += utcOff * 60;
struct tm *tm = gmtime(&now);
size_t len = strftime(buf, PATH_MAX, fmt, tm);
if (!len)
return false;
free(watermarkInfo.src);
const uint32_t h = fontsize + 4;
const uint32_t w = drawnwidth(buf);
watermarkInfo.w = w;
watermarkInfo.h = h;
watermarkInfo.src = (uint8_t *) calloc(w, h);
str(watermarkInfo.src, buf, 0, fontsize, w, h, w);
return true;
}
bool watermarkInit() { bool watermarkInit() {
memset(&watermarkInfo, 0, sizeof(watermarkInfo_t)); memset(&watermarkInfo, 0, sizeof(watermarkInfo_t));
watermarkData = watermarkUnpacked = watermarkTmp = NULL; watermarkData = watermarkUnpacked = watermarkTmp = NULL;
rw = rh = 0; rw = rh = 0;
if (!Server::DLP_WatermarkImage[0]) if (!Server::DLP_WatermarkImage[0] && !Server::DLP_WatermarkText[0])
return true; return true;
if (!loadimage(Server::DLP_WatermarkImage)) if (Server::DLP_WatermarkImage[0] && Server::DLP_WatermarkText[0]) {
vlog.error("WatermarkImage and WatermarkText can't be used together");
return false;
}
if (Server::DLP_WatermarkImage[0] && !loadimage(Server::DLP_WatermarkImage))
return false;
if (Server::DLP_WatermarkText[0] &&
!drawtext(Server::DLP_WatermarkText,
Server::DLP_WatermarkTimeOffset * 60 + Server::DLP_WatermarkTimeOffsetMinutes,
Server::DLP_WatermarkFont, Server::DLP_WatermarkFontSize))
return false; return false;
if (Server::DLP_WatermarkRepeatSpace && Server::DLP_WatermarkLocation[0]) { if (Server::DLP_WatermarkRepeatSpace && Server::DLP_WatermarkLocation[0]) {
@ -136,10 +280,22 @@ bool watermarkInit() {
} }
// update the screen-size rendered watermark whenever the screen is resized // update the screen-size rendered watermark whenever the screen is resized
// or if using text, every frame
void VNCServerST::updateWatermark() { void VNCServerST::updateWatermark() {
if (rw == pb->width() && if (rw == pb->width() &&
rh == pb->height()) rh == pb->height()) {
if (Server::DLP_WatermarkImage[0])
return; return;
if (!watermarkTextNeedsUpdate(false))
return;
}
if (Server::DLP_WatermarkText[0] && watermarkTextNeedsUpdate(false)) {
drawtext(Server::DLP_WatermarkText,
Server::DLP_WatermarkTimeOffset * 60 + Server::DLP_WatermarkTimeOffsetMinutes,
Server::DLP_WatermarkFont, Server::DLP_WatermarkFontSize);
}
rw = pb->width(); rw = pb->width();
rh = pb->height(); rh = pb->height();
@ -246,3 +402,15 @@ void packWatermark(const Region &changed) {
watermarkDataLen = destLen; watermarkDataLen = destLen;
} }
// Limit changes to once per second
bool watermarkTextNeedsUpdate(const bool early) {
static time_t now;
// We're called a couple times per frame, only grab the
// time on the first time so it doesn't change inside a frame
if (early)
now = time(NULL);
return now != lastUpdate;
}

View File

@ -36,6 +36,7 @@ extern watermarkInfo_t watermarkInfo;
bool watermarkInit(); bool watermarkInit();
void packWatermark(const rfb::Region &changed); // filter and pack the watermark for sending void packWatermark(const rfb::Region &changed); // filter and pack the watermark for sending
bool watermarkTextNeedsUpdate(const bool early);
extern uint8_t *watermarkData; extern uint8_t *watermarkData;
extern uint32_t watermarkDataLen; extern uint32_t watermarkDataLen;

3107
common/rfb/font.h Normal file

File diff suppressed because it is too large Load Diff

2
debian/control vendored
View File

@ -14,7 +14,7 @@ Architecture: amd64 arm64
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth, Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth,
x11-xkb-utils, xkb-data, procps, libswitch-perl, libyaml-tiny-perl, x11-xkb-utils, xkb-data, procps, libswitch-perl, libyaml-tiny-perl,
libhash-merge-simple-perl, libscalar-list-utils-perl, liblist-moreutils-perl, libhash-merge-simple-perl, libscalar-list-utils-perl, liblist-moreutils-perl,
libtry-tiny-perl, libgbm1 libtry-tiny-perl, libdatetime-timezone-perl, libgbm1
Provides: vnc-server Provides: vnc-server
Description: KasmVNC provides remote web-based access to a Desktop or application. Description: KasmVNC provides remote web-based access to a Desktop or application.
While VNC is in the name, KasmVNC differs from other VNC variants such While VNC is in the name, KasmVNC differs from other VNC variants such

View File

@ -7,7 +7,7 @@ License: GPLv2+
URL: https://github.com/kasmtech/KasmVNC URL: https://github.com/kasmtech/KasmVNC
BuildRequires: rsync BuildRequires: rsync
Requires: xorg-x11-xauth, xkeyboard-config, xkbcomp, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, mesa-libgbm, libxshmfence Requires: xorg-x11-xauth, xkeyboard-config, xkbcomp, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, perl-DateTime-TimeZone, mesa-libgbm, libxshmfence
Conflicts: tigervnc-server, tigervnc-server-minimal Conflicts: tigervnc-server, tigervnc-server-minimal
%description %description

View File

@ -7,7 +7,7 @@ License: GPLv2+
URL: https://github.com/kasmtech/KasmVNC URL: https://github.com/kasmtech/KasmVNC
BuildRequires: rsync BuildRequires: rsync
Requires: xauth, hostname, libxkbcommon-x11-0, xkeyboard-config, x11-tools, openssl, perl, libpixman-1-0, libjpeg8, libgomp1, libXfont2-2, libXdmcp6, libglvnd, xkbcomp, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, libgbm1, libxshmfence1 Requires: xauth, hostname, libxkbcommon-x11-0, xkeyboard-config, x11-tools, openssl, perl, libpixman-1-0, libjpeg8, libgomp1, libXfont2-2, libXdmcp6, libglvnd, xkbcomp, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, perl-DateTime-TimeZone, libgbm1, libxshmfence1
Conflicts: tigervnc, tigervnc-x11vnc Conflicts: tigervnc, tigervnc-x11vnc
%description %description

View File

@ -7,7 +7,7 @@ License: GPLv2+
URL: https://github.com/kasmtech/KasmVNC URL: https://github.com/kasmtech/KasmVNC
BuildRequires: rsync BuildRequires: rsync
Requires: xorg-x11-xauth, xorg-x11-xkb-utils, xkeyboard-config, xorg-x11-server-utils, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, hostname, mesa-libgbm, libxshmfence Requires: xorg-x11-xauth, xorg-x11-xkb-utils, xkeyboard-config, xorg-x11-server-utils, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, perl-DateTime-TimeZone, hostname, mesa-libgbm, libxshmfence
Conflicts: tigervnc-server, tigervnc-server-minimal Conflicts: tigervnc-server, tigervnc-server-minimal
%description %description

View File

@ -7,7 +7,7 @@ License: GPLv2+
URL: https://github.com/kasmtech/KasmVNC URL: https://github.com/kasmtech/KasmVNC
BuildRequires: rsync BuildRequires: rsync
Requires: xorg-x11-xauth, xkeyboard-config, xorg-x11-server-utils, xkbcomp, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, hostname, mesa-libgbm, libxshmfence Requires: xorg-x11-xauth, xkeyboard-config, xorg-x11-server-utils, xkbcomp, openssl, perl, perl-Switch, perl-YAML-Tiny, perl-Hash-Merge-Simple, perl-Scalar-List-Utils, perl-List-MoreUtils, perl-Try-Tiny, perl-DateTime-TimeZone, hostname, mesa-libgbm, libxshmfence
Conflicts: tigervnc-server, tigervnc-server-minimal Conflicts: tigervnc-server, tigervnc-server-minimal
%description %description

View File

@ -2,7 +2,7 @@ import os
import re import re
import shutil import shutil
from os.path import expanduser from os.path import expanduser
from mamba import description, context, fcontext, it, fit, before, after from mamba import description, context, fcontext, it, fit, _it, before, after
from expects import expect, equal, contain, match from expects import expect, equal, contain, match
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \ from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
@ -268,6 +268,18 @@ with description('YAML to CLI') as self:
completed_process.stdout) completed_process.stdout)
expect(cli_option).to(equal("-geometry '1024x768'")) expect(cli_option).to(equal("-geometry '1024x768'"))
with it("allows wide utf characters"):
write_config('''
data_loss_prevention:
watermark:
text:
template: "星街すいせい"
''')
completed_process = run_vncserver()
cli_option = pick_cli_option('DLP_WatermarkText',
completed_process.stdout)
expect(cli_option).to(equal("-DLP_WatermarkText '星街すいせい'"))
with it("ignores empty section override"): with it("ignores empty section override"):
write_config(''' write_config('''
security: security:

9
t Normal file
View File

@ -0,0 +1,9 @@
use DateTime::TimeZone;
my $timezone = $ARGV[0];
if (DateTime::TimeZone->is_valid_name($timezone)) {
print "Valid timezone\n";
} else {
print "Invalid timezone\n";
}

11
t2 Normal file
View File

@ -0,0 +1,11 @@
use DateTime;
use DateTime::TimeZone;
#my $timezone_name = 'America/New_York';
my $timezone_name = 'UTC';
my $dt = DateTime->now(time_zone => $timezone_name);
my $offset = $dt->offset();
print "Timezone: $timezone_name\n";
print "Offset: $offset seconds\n";

View File

@ -0,0 +1,37 @@
package KasmVNC::CallbackValidator;
use strict;
use warnings;
use v5.10;
use Data::Dumper;
use KasmVNC::Utils;
sub new {
my ($class, $args) = @_;
my $self = bless {
isValidCallback => $args->{isValidCallback},
errorMessage => $args->{errorMessage}
}, $class;
}
sub validate {
my $self = shift;
$self->{configKey} = shift;
my @values = @{ listify($self->{configKey}->value()) };
foreach my $value (@values) {
$self->validateValue($value);
}
}
sub validateValue {
my $self = shift;
my $value = shift;
unless ($self->{isValidCallback}($value)) {
$self->{configKey}->addErrorMessage($self->{errorMessage});
}
}
1;

View File

@ -45,6 +45,8 @@ sub isPresent {
sub deriveBoolean { sub deriveBoolean {
my $value = shift; my $value = shift;
return $value if containsWideSymbols($value);
switch($value) { switch($value) {
case 'true' { case 'true' {
return 1; return 1;
@ -63,4 +65,12 @@ sub printStackTrace {
print { *STDERR } $trace->as_string; print { *STDERR } $trace->as_string;
} }
sub containsWideSymbols {
my $string = shift;
return 1 unless defined($string);
$string =~ /[^\x00-\xFF]/;
}
1; 1;

View File

@ -92,6 +92,11 @@ data_loss_prevention:
# location: 10,10 # location: 10,10
# tint: 255,20,20,128 # tint: 255,20,20,128
# repeat_spacing: 10 # repeat_spacing: 10
text:
template: "${USER} %H:%M"
font: auto
font_size: 48
timezone_name: Australia/Adelaide
logging: logging:
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard content # "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard content
level: off level: off

View File

@ -27,6 +27,7 @@
use v5.10; use v5.10;
use warnings; use warnings;
use utf8;
sub DEVENV() { $ENV{KASMVNC_DEVELOPMENT} }; sub DEVENV() { $ENV{KASMVNC_DEVELOPMENT} };
use if DEVENV, Devel::StackTrace; use if DEVENV, Devel::StackTrace;
@ -38,11 +39,14 @@ use List::Util qw(first);
use List::MoreUtils qw(any uniq); use List::MoreUtils qw(any uniq);
use Data::Dumper; use Data::Dumper;
use Try::Tiny; use Try::Tiny;
use DateTime;
use DateTime::TimeZone;
use KasmVNC::CliOption; use KasmVNC::CliOption;
use KasmVNC::ConfigKey; use KasmVNC::ConfigKey;
use KasmVNC::PatternValidator; use KasmVNC::PatternValidator;
use KasmVNC::EnumValidator; use KasmVNC::EnumValidator;
use KasmVNC::CallbackValidator;
use KasmVNC::Config; use KasmVNC::Config;
use KasmVNC::Users; use KasmVNC::Users;
use KasmVNC::TextOption; use KasmVNC::TextOption;
@ -56,6 +60,7 @@ use constant {
OPTIONAL_ARG_VALUE => 2 OPTIONAL_ARG_VALUE => 2
}; };
UseUtfStdio();
InitLogger(); InitLogger();
CheckWeCanRunInThisEnvironment(); CheckWeCanRunInThisEnvironment();
@ -1765,6 +1770,79 @@ sub DefineConfigToCLIConversion {
}) })
] ]
}), }),
KasmVNC::CliOption->new({
name => 'DLP_WatermarkText',
configKeys => [
KasmVNC::ConfigKey->new({
name => "data_loss_prevention.watermark.text.template",
validator => KasmVNC::CallbackValidator->new({
isValidCallback => sub {
my $value = shift;
isBlank(ConfigValue("data_loss_prevention.watermark.image"));
},
errorMessage => "Watermark image and text can't be used at the same time"
}),
})
]
}),
KasmVNC::CliOption->new({
name => 'DLP_WatermarkFont',
configKeys => [
KasmVNC::ConfigKey->new({
name => "data_loss_prevention.watermark.text.font",
type => KasmVNC::ConfigKey::ANY
})
],
isActiveSub => sub {
$self = shift;
my $value = $self->configValue();
isPresent($value) && $value ne "auto";
}
}),
KasmVNC::CliOption->new({
name => 'DLP_WatermarkFontSize',
configKeys => [
KasmVNC::ConfigKey->new({
name => "data_loss_prevention.watermark.text.font_size",
validator => KasmVNC::CallbackValidator->new({
isValidCallback => sub {
my $value = shift;
return 0 unless $value =~ /^\d+$/;
$value >= 8 && $value <= 256;
},
errorMessage => "must be in range 8..256"
}),
})
]
}),
KasmVNC::CliOption->new({
name => 'DLP_WatermarkTimeOffsetMinutes',
configKeys => [
KasmVNC::ConfigKey->new({
name => "data_loss_prevention.watermark.text.timezone_name",
validator => KasmVNC::CallbackValidator->new({
isValidCallback => sub {
my $timezone_name = shift;
DateTime::TimeZone->is_valid_name($timezone_name);
},
errorMessage => "must be a valid timezone name like Australia/Adelaide"
})
})
],
deriveValueSub => sub {
my $self = shift;
my $timezone_name = $self->configValue();
my $dt = DateTime->now(time_zone => $timezone_name);
my $offset_in_seconds = $dt->offset();
$offset_in_seconds/60;
}
}),
KasmVNC::CliOption->new({ KasmVNC::CliOption->new({
name => 'DLP_Log', name => 'DLP_Log',
configKeys => [ configKeys => [
@ -2835,3 +2913,7 @@ sub InitLogger {
my $debugEnabled = any { $_ eq "-debug" } @ARGV; my $debugEnabled = any { $_ eq "-debug" } @ARGV;
$logger = KasmVNC::Logger->new({ level => $debugEnabled ? "debug" : "warn" }); $logger = KasmVNC::Logger->new({ level => $debugEnabled ? "debug" : "warn" });
} }
sub UseUtfStdio {
use open qw( :std :encoding(UTF-8) );
}

View File

@ -52,7 +52,8 @@ Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DKASMVNC -DNO_MODULE_EXTS \
-I$(top_srcdir)/dri3 @LIBDRM_CFLAGS@ -I$(top_srcdir)/dri3 @LIBDRM_CFLAGS@
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \ Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt $(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt \
-lfreetype
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp

View File

@ -243,6 +243,8 @@ Default \fB-1\fP.
.B \-WebpVideoQuality \fInum\fP .B \-WebpVideoQuality \fInum\fP
The WEBP quality to use when in video mode. The WEBP quality to use when in video mode.
Default \fB-1\fP. Default \fB-1\fP.
.
.TP
.B \-MaxVideoResolution \fI1920x1080\fP .B \-MaxVideoResolution \fI1920x1080\fP
When in video mode, downscale the screen to max this size. Keeps aspect ratio. When in video mode, downscale the screen to max this size. Keeps aspect ratio.
Default \fB1920x1080\fP. Default \fB1920x1080\fP.
@ -372,6 +374,28 @@ The color components can be used to colorize the greyscale watermark, and the al
can be used to make it fainter. can be used to make it fainter.
. .
.TP .TP
.B \-DLP_WatermarkText \fI"foo %H:%M"\fP
Instead of an image, render this text as the watermark. Takes time formatting options
for \fBstrftime\fP.
.
.TP
.B \-DLP_WatermarkFont \fI/path/to/font.ttf\fP
Use a different font for -DLP_WatermarkText than the bundled one. TTF and OTF fonts
are accepted.
.
.TP
.B \-DLP_WatermarkFontSize \fI48\fP
Font size for -DLP_WatermarkText. Default \fI48\fP.
.
.TP
.B \-DLP_WatermarkTimeOffset \fI0\fP
Time offset from UTC, hours. Default \fI0\fP.
.
.TP
.B \-DLP_WatermarkTimeOffsetMinutes \fI0\fP
Time offset from UTC, minutes. Default \fI0\fP.
.
.TP
.B \-selfBench .B \-selfBench
Run a set of self-benchmarks and exit. Run a set of self-benchmarks and exit.
. .