@@ -77,7 +77,7 @@ bool FdInStream::fillBuffer() | |||
// returning EINTR. | |||
// | |||
size_t FdInStream::readFd(void* buf, size_t len) | |||
size_t FdInStream::readFd(uint8_t* buf, size_t len) | |||
{ | |||
int n; | |||
do { |
@@ -39,7 +39,7 @@ namespace rdr { | |||
private: | |||
virtual bool fillBuffer(); | |||
size_t readFd(void* buf, size_t len); | |||
size_t readFd(uint8_t* buf, size_t len); | |||
int fd; | |||
bool closeWhenDone; |
@@ -83,7 +83,7 @@ void FdOutStream::cork(bool enable) | |||
bool FdOutStream::flushBuffer() | |||
{ | |||
size_t n = writeFd((const void*) sentUpTo, ptr - sentUpTo); | |||
size_t n = writeFd(sentUpTo, ptr - sentUpTo); | |||
if (n == 0) | |||
return false; | |||
@@ -101,7 +101,7 @@ bool FdOutStream::flushBuffer() | |||
// returning EINTR. | |||
// | |||
size_t FdOutStream::writeFd(const void* data, size_t length) | |||
size_t FdOutStream::writeFd(const uint8_t* data, size_t length) | |||
{ | |||
int n; | |||
@@ -45,7 +45,7 @@ namespace rdr { | |||
private: | |||
virtual bool flushBuffer(); | |||
size_t writeFd(const void* data, size_t length); | |||
size_t writeFd(const uint8_t* data, size_t length); | |||
int fd; | |||
struct timeval lastWrite; | |||
}; |
@@ -145,7 +145,7 @@ namespace rdr { | |||
// readBytes() reads an exact number of bytes. | |||
void readBytes(void* data, size_t length) { | |||
void readBytes(uint8_t* data, size_t length) { | |||
check(length); | |||
memcpy(data, ptr, length); | |||
ptr += length; |
@@ -36,8 +36,8 @@ namespace rdr { | |||
public: | |||
MemInStream(const void* data, size_t len, bool deleteWhenDone_=false) | |||
: start((const uint8_t*)data), deleteWhenDone(deleteWhenDone_) | |||
MemInStream(const uint8_t* data, size_t len, bool deleteWhenDone_=false) | |||
: start(data), deleteWhenDone(deleteWhenDone_) | |||
{ | |||
ptr = start; | |||
end = start + len; |
@@ -48,7 +48,7 @@ namespace rdr { | |||
// data() returns a pointer to the buffer. | |||
const void* data() { return (const void*)start; } | |||
const uint8_t* data() { return start; } | |||
protected: | |||
@@ -70,7 +70,7 @@ namespace rdr { | |||
// writeBytes() writes an exact number of bytes. | |||
void writeBytes(const void* data, size_t length) { | |||
void writeBytes(const uint8_t* data, size_t length) { | |||
while (length > 0) { | |||
check(1); | |||
size_t n = length; |
@@ -53,7 +53,7 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) | |||
if (in->avail() < size) | |||
size = in->avail(); | |||
in->readBytes(data, size); | |||
in->readBytes((uint8_t*)data, size); | |||
} catch (EndOfStream&) { | |||
return 0; | |||
} catch (SystemException &e) { |
@@ -44,7 +44,7 @@ ssize_t TLSOutStream::push(gnutls_transport_ptr_t str, const void* data, | |||
self->saved_exception = NULL; | |||
try { | |||
out->writeBytes(data, size); | |||
out->writeBytes((const uint8_t*)data, size); | |||
out->flush(); | |||
} catch (SystemException &e) { | |||
vlog.error("Failure sending TLS data: %s", e.str()); |
@@ -163,7 +163,7 @@ bool CConnection::processVersionMsg() | |||
if (!is->hasData(12)) | |||
return false; | |||
is->readBytes(verStr, 12); | |||
is->readBytes((uint8_t*)verStr, 12); | |||
verStr[12] = '\0'; | |||
if (sscanf(verStr, "RFB %03d.%03d\n", | |||
@@ -192,7 +192,7 @@ bool CConnection::processVersionMsg() | |||
sprintf(verStr, "RFB %03d.%03d\n", | |||
server.majorVersion, server.minorVersion); | |||
os->writeBytes(verStr, 12); | |||
os->writeBytes((const uint8_t*)verStr, 12); | |||
os->flush(); | |||
state_ = RFBSTATE_SECURITY_TYPES; | |||
@@ -361,7 +361,7 @@ bool CConnection::processSecurityReasonMsg() | |||
is->clearRestorePoint(); | |||
std::vector<char> reason(len + 1); | |||
is->readBytes(reason.data(), len); | |||
is->readBytes((uint8_t*)reason.data(), len); | |||
reason[len] = '\0'; | |||
state_ = RFBSTATE_INVALID; | |||
@@ -543,7 +543,7 @@ void CConnection::serverCutText(const char* str) | |||
{ | |||
hasLocalClipboard = false; | |||
serverClipboard = latin1ToUTF8(str); | |||
serverClipboard = str; | |||
hasRemoteClipboard = true; | |||
handleClipboardAnnounce(true); | |||
@@ -604,6 +604,11 @@ void CConnection::handleClipboardProvide(uint32_t flags, | |||
return; | |||
} | |||
// FIXME: This conversion magic should be in CMsgReader | |||
if (!isValidUTF8((const char*)data[0], lengths[0])) { | |||
vlog.error("Invalid UTF-8 sequence in clipboard - ignoring"); | |||
return; | |||
} | |||
serverClipboard = convertLF((const char*)data[0], lengths[0]); | |||
hasRemoteClipboard = true; | |||
@@ -674,6 +679,7 @@ void CConnection::announceClipboard(bool available) | |||
void CConnection::sendClipboardData(const char* data) | |||
{ | |||
if (server.clipboardFlags() & rfb::clipboardProvide) { | |||
// FIXME: This conversion magic should be in CMsgWriter | |||
std::string filtered(convertCRLF(data)); | |||
size_t sizes[1] = { filtered.size() + 1 }; | |||
const uint8_t* data[1] = { (const uint8_t*)filtered.c_str() }; | |||
@@ -690,9 +696,7 @@ void CConnection::sendClipboardData(const char* data) | |||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | |||
} else { | |||
std::string latin1(utf8ToLatin1(data)); | |||
writer()->writeClientCutText(latin1.c_str()); | |||
writer()->writeClientCutText(data); | |||
} | |||
} | |||
@@ -747,7 +751,7 @@ void CConnection::setPF(const PixelFormat& pf) | |||
formatChange = true; | |||
} | |||
void CConnection::fence(uint32_t flags, unsigned len, const char data[]) | |||
void CConnection::fence(uint32_t flags, unsigned len, const uint8_t data[]) | |||
{ | |||
CMsgHandler::fence(flags, len, data); | |||
@@ -249,7 +249,7 @@ namespace rfb { | |||
// responds to requests, stating no support for synchronisation. | |||
// When overriding, call CMsgHandler::fence() directly in order to | |||
// state correct support for fence flags. | |||
virtual void fence(uint32_t flags, unsigned len, const char data[]); | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
private: | |||
bool processVersionMsg(); |
@@ -70,7 +70,7 @@ void CMsgHandler::setName(const char* name) | |||
} | |||
void CMsgHandler::fence(uint32_t /*flags*/, unsigned /*len*/, | |||
const char /*data*/ []) | |||
const uint8_t /*data*/ []) | |||
{ | |||
server.supportsFence = true; | |||
} |
@@ -55,7 +55,7 @@ namespace rfb { | |||
virtual void setCursorPos(const Point& pos) = 0; | |||
virtual void setPixelFormat(const PixelFormat& pf); | |||
virtual void setName(const char* name); | |||
virtual void fence(uint32_t flags, unsigned len, const char data[]); | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
virtual void endOfContinuousUpdates(); | |||
virtual void supportsQEMUKeyEvent(); | |||
virtual void serverInit(int width, int height, |
@@ -74,9 +74,14 @@ bool CMsgReader::readServerInit() | |||
return false; | |||
is->clearRestorePoint(); | |||
std::vector<char> name(len + 1); | |||
is->readBytes(name.data(), len); | |||
is->readBytes((uint8_t*)name.data(), len); | |||
name[len] = '\0'; | |||
handler->serverInit(width, height, pf, name.data()); | |||
if (isValidUTF8(name.data())) | |||
handler->serverInit(width, height, pf, name.data()); | |||
else | |||
handler->serverInit(width, height, pf, | |||
latin1ToUTF8(name.data()).c_str()); | |||
return true; | |||
} | |||
@@ -275,9 +280,13 @@ bool CMsgReader::readServerCutText() | |||
vlog.error("cut text too long (%d bytes) - ignoring",len); | |||
return true; | |||
} | |||
std::vector<char> ca(len); | |||
is->readBytes(ca.data(), len); | |||
std::string filtered(convertLF(ca.data(), len)); | |||
is->readBytes((uint8_t*)ca.data(), len); | |||
std::string utf8(latin1ToUTF8(ca.data(), ca.size())); | |||
std::string filtered(convertLF(utf8.data(), utf8.size())); | |||
handler->serverCutText(filtered.c_str()); | |||
return true; | |||
@@ -409,7 +418,7 @@ bool CMsgReader::readFence() | |||
{ | |||
uint32_t flags; | |||
uint8_t len; | |||
char data[64]; | |||
uint8_t data[64]; | |||
if (!is->hasData(3 + 4 + 1)) | |||
return false; | |||
@@ -763,15 +772,21 @@ bool CMsgReader::readSetDesktopName(int x, int y, int w, int h) | |||
is->clearRestorePoint(); | |||
std::vector<char> name(len + 1); | |||
is->readBytes(name.data(), len); | |||
is->readBytes((uint8_t*)name.data(), len); | |||
name[len] = '\0'; | |||
if (x || y || w || h) { | |||
vlog.error("Ignoring DesktopName rect with non-zero position/size"); | |||
} else { | |||
handler->setName(name.data()); | |||
return true; | |||
} | |||
if (!isValidUTF8(name.data())) { | |||
vlog.error("Ignoring DesktopName rect with invalid UTF-8 sequence"); | |||
return true; | |||
} | |||
handler->setName(name.data()); | |||
return true; | |||
} | |||
@@ -36,6 +36,7 @@ | |||
#include <rfb/Rect.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/CMsgWriter.h> | |||
#include <rfb/util.h> | |||
using namespace rfb; | |||
@@ -130,7 +131,7 @@ void CMsgWriter::writeEnableContinuousUpdates(bool enable, | |||
endMsg(); | |||
} | |||
void CMsgWriter::writeFence(uint32_t flags, unsigned len, const char data[]) | |||
void CMsgWriter::writeFence(uint32_t flags, unsigned len, const uint8_t data[]) | |||
{ | |||
if (!server->supportsFence) | |||
throw Exception("Server does not support fences"); | |||
@@ -191,16 +192,15 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) | |||
void CMsgWriter::writeClientCutText(const char* str) | |||
{ | |||
size_t len; | |||
if (strchr(str, '\r') != NULL) | |||
throw Exception("Invalid carriage return in clipboard data"); | |||
len = strlen(str); | |||
std::string latin1(utf8ToLatin1(str)); | |||
startMsg(msgTypeClientCutText); | |||
os->pad(3); | |||
os->writeU32(len); | |||
os->writeBytes(str, len); | |||
os->writeU32(latin1.size()); | |||
os->writeBytes((const uint8_t*)latin1.data(), latin1.size()); | |||
endMsg(); | |||
} | |||
@@ -51,7 +51,7 @@ namespace rfb { | |||
void writeFramebufferUpdateRequest(const Rect& r,bool incremental); | |||
void writeEnableContinuousUpdates(bool enable, int x, int y, int w, int h); | |||
void writeFence(uint32_t flags, unsigned len, const char data[]); | |||
void writeFence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
void writeKeyEvent(uint32_t keysym, uint32_t keycode, bool down); | |||
void writePointerEvent(const Point& pos, int buttonMask); |
@@ -130,7 +130,7 @@ void CSecurityDH::writeCredentials() | |||
struct aes128_ctx aesCtx; | |||
aes128_set_encrypt_key(&aesCtx, key); | |||
char buf[128]; | |||
uint8_t buf[128]; | |||
if (!rs.hasData(128)) | |||
throw ConnFailedException("failed to generate random padding"); | |||
rs.readBytes(buf, 128); | |||
@@ -140,7 +140,7 @@ void CSecurityDH::writeCredentials() | |||
if (password.size() >= 64) | |||
throw AuthFailureException("password is too long"); | |||
memcpy(buf + 64, password.c_str(), password.size() + 1); | |||
aes128_encrypt(&aesCtx, 128, (uint8_t *)buf, (uint8_t *)buf); | |||
aes128_encrypt(&aesCtx, 128, buf, buf); | |||
rdr::OutStream* os = cc->getOutStream(); | |||
os->writeBytes(buf, 128); |
@@ -41,8 +41,8 @@ bool CSecurityPlain::processMsg() | |||
// Return the response to the server | |||
os->writeU32(username.size()); | |||
os->writeU32(password.size()); | |||
os->writeBytes(username.data(), username.size()); | |||
os->writeBytes(password.data(), password.size()); | |||
os->writeBytes((const uint8_t*)username.data(), username.size()); | |||
os->writeBytes((const uint8_t*)password.data(), password.size()); | |||
os->flush(); | |||
return true; | |||
} |
@@ -445,7 +445,7 @@ void CSecurityRSAAES::writeCredentials() | |||
if (username.size() > 255) | |||
throw AuthFailureException("username is too long"); | |||
raos->writeU8(username.size()); | |||
raos->writeBytes(username.data(), username.size()); | |||
raos->writeBytes((const uint8_t*)username.data(), username.size()); | |||
} else { | |||
raos->writeU8(0); | |||
} | |||
@@ -453,6 +453,6 @@ void CSecurityRSAAES::writeCredentials() | |||
if (password.size() > 255) | |||
throw AuthFailureException("password is too long"); | |||
raos->writeU8(password.size()); | |||
raos->writeBytes(password.data(), password.size()); | |||
raos->writeBytes((const uint8_t*)password.data(), password.size()); | |||
raos->flush(); | |||
} |
@@ -49,7 +49,7 @@ bool CopyRectDecoder::readRect(const Rect& /*r*/, | |||
void CopyRectDecoder::getAffectedRegion(const Rect& rect, | |||
const void* buffer, | |||
const uint8_t* buffer, | |||
size_t buflen, | |||
const ServerParams& server, | |||
Region* region) | |||
@@ -64,7 +64,7 @@ void CopyRectDecoder::getAffectedRegion(const Rect& rect, | |||
srcY-rect.tl.y)))); | |||
} | |||
void CopyRectDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void CopyRectDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, | |||
const ServerParams& /*server*/, | |||
ModifiablePixelBuffer* pb) |
@@ -28,10 +28,10 @@ namespace rfb { | |||
virtual ~CopyRectDecoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void getAffectedRegion(const Rect& rect, const void* buffer, | |||
virtual void getAffectedRegion(const Rect& rect, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
Region* region); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; |
@@ -46,7 +46,7 @@ Decoder::~Decoder() | |||
} | |||
void Decoder::getAffectedRegion(const Rect& rect, | |||
const void* /*buffer*/, | |||
const uint8_t* /*buffer*/, | |||
size_t /*buflen*/, | |||
const ServerParams& /*server*/, | |||
Region* region) | |||
@@ -55,10 +55,10 @@ void Decoder::getAffectedRegion(const Rect& rect, | |||
} | |||
bool Decoder::doRectsConflict(const Rect& /*rectA*/, | |||
const void* /*bufferA*/, | |||
const uint8_t* /*bufferA*/, | |||
size_t /*buflenA*/, | |||
const Rect& /*rectB*/, | |||
const void* /*bufferB*/, | |||
const uint8_t* /*bufferB*/, | |||
size_t /*buflenB*/, | |||
const ServerParams& /*server*/) | |||
{ |
@@ -19,6 +19,8 @@ | |||
#ifndef __RFB_DECODER_H__ | |||
#define __RFB_DECODER_H__ | |||
#include <stdint.h> | |||
namespace rdr { | |||
class InStream; | |||
class OutStream; | |||
@@ -62,7 +64,7 @@ namespace rfb { | |||
// getAffectedRegion() returns the parts of the frame buffer will | |||
// be either read from or written do when decoding this rect. The | |||
// default implementation simply returns the given rectangle. | |||
virtual void getAffectedRegion(const Rect& rect, const void* buffer, | |||
virtual void getAffectedRegion(const Rect& rect, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
Region* region); | |||
@@ -70,10 +72,10 @@ namespace rfb { | |||
// in the order they were received. This will only be called if the | |||
// DecoderPartiallyOrdered flag has been set. | |||
virtual bool doRectsConflict(const Rect& rectA, | |||
const void* bufferA, | |||
const uint8_t* bufferA, | |||
size_t buflenA, | |||
const Rect& rectB, | |||
const void* bufferB, | |||
const uint8_t* bufferB, | |||
size_t buflenB, | |||
const ServerParams& server); | |||
@@ -81,7 +83,7 @@ namespace rfb { | |||
// given buffer, onto the ModifiablePixelBuffer. The PixelFormat of | |||
// the PixelBuffer might not match the ConnParams and it is up to | |||
// the decoder to do any necessary conversion. | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb)=0; | |||
@@ -93,7 +93,7 @@ bool H264Decoder::readRect(const Rect& /*r*/, | |||
return true; | |||
} | |||
void H264Decoder::decodeRect(const Rect& r, const void* buffer, | |||
void H264Decoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, | |||
const ServerParams& /*server*/, | |||
ModifiablePixelBuffer* pb) |
@@ -35,7 +35,7 @@ namespace rfb { | |||
virtual ~H264Decoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
@@ -113,7 +113,7 @@ bool HextileDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
return true; | |||
} | |||
void HextileDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void HextileDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
@@ -158,7 +158,7 @@ void HextileDecoder::hextileDecode(const Rect& r, rdr::InStream* is, | |||
int tileType = is->readU8(); | |||
if (tileType & hextileRaw) { | |||
is->readBytes(buf, t.area() * sizeof(T)); | |||
is->readBytes((uint8_t*)buf, t.area() * sizeof(T)); | |||
pb->imageRect(pf, t, buf); | |||
continue; | |||
} |
@@ -31,7 +31,7 @@ namespace rfb { | |||
virtual ~HextileDecoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
private: |
@@ -161,7 +161,8 @@ void HextileEncoder::hextileEncode(rdr::OutStream* os, | |||
if (encodedLen < 0) { | |||
pb->getImage(buf, t); | |||
os->writeU8(hextileRaw); | |||
os->writeBytes(buf, t.width() * t.height() * sizeof(T)); | |||
os->writeBytes((const uint8_t*)buf, | |||
t.width() * t.height() * sizeof(T)); | |||
oldBgValid = oldFgValid = false; | |||
continue; | |||
} | |||
@@ -557,7 +558,8 @@ void HextileEncoder::hextileEncodeBetter(rdr::OutStream* os, | |||
if ( (tileType & hextileRaw) != 0 || | |||
encodedLen >= t.width() * t.height() * sizeof(T)) { | |||
os->writeU8(hextileRaw); | |||
os->writeBytes(buf, t.width() * t.height() * sizeof(T)); | |||
os->writeBytes((const uint8_t*)buf, | |||
t.width() * t.height() * sizeof(T)); | |||
oldBgValid = oldFgValid = false; | |||
continue; | |||
} |
@@ -254,7 +254,7 @@ void JpegCompressor::compress(const uint8_t *buf, volatile int stride, | |||
delete[] rowPointer; | |||
} | |||
void JpegCompressor::writeBytes(const void* /*data*/, int /*length*/) | |||
void JpegCompressor::writeBytes(const uint8_t* /*data*/, int /*length*/) | |||
{ | |||
throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead."); | |||
} |
@@ -45,7 +45,7 @@ namespace rfb { | |||
void compress(const uint8_t *, int, const Rect&, const PixelFormat&, int, int); | |||
void writeBytes(const void*, int); | |||
void writeBytes(const uint8_t*, int); | |||
private: | |||
@@ -66,7 +66,7 @@ bool RREDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, | |||
return true; | |||
} | |||
void RREDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void RREDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ |
@@ -31,7 +31,7 @@ namespace rfb { | |||
virtual ~RREDecoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
private: |
@@ -46,7 +46,7 @@ bool RawDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
return true; | |||
} | |||
void RawDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void RawDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ |
@@ -27,7 +27,7 @@ namespace rfb { | |||
virtual ~RawDecoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; |
@@ -88,7 +88,7 @@ void SConnection::initialiseProtocol() | |||
char str[13]; | |||
sprintf(str, "RFB %03d.%03d\n", defaultMajorVersion, defaultMinorVersion); | |||
os->writeBytes(str, 12); | |||
os->writeBytes((const uint8_t*)str, 12); | |||
os->flush(); | |||
state_ = RFBSTATE_PROTOCOL_VERSION; | |||
@@ -126,7 +126,7 @@ bool SConnection::processVersionMsg() | |||
if (!is->hasData(12)) | |||
return false; | |||
is->readBytes(verStr, 12); | |||
is->readBytes((uint8_t*)verStr, 12); | |||
verStr[12] = '\0'; | |||
if (sscanf(verStr, "RFB %03d.%03d\n", | |||
@@ -298,7 +298,8 @@ bool SConnection::handleAuthFailureTimeout(Timer* /*t*/) | |||
os->writeU32(secResultFailed); | |||
if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message | |||
os->writeU32(authFailureMsg.size()); | |||
os->writeBytes(authFailureMsg.data(), authFailureMsg.size()); | |||
os->writeBytes((const uint8_t*)authFailureMsg.data(), | |||
authFailureMsg.size()); | |||
} | |||
os->flush(); | |||
} catch (rdr::Exception& e) { | |||
@@ -326,12 +327,12 @@ void SConnection::throwConnFailedException(const char* format, ...) | |||
if (client.majorVersion == 3 && client.minorVersion == 3) { | |||
os->writeU32(0); | |||
os->writeU32(strlen(str)); | |||
os->writeBytes(str, strlen(str)); | |||
os->writeBytes((const uint8_t*)str, strlen(str)); | |||
os->flush(); | |||
} else { | |||
os->writeU8(0); | |||
os->writeU32(strlen(str)); | |||
os->writeBytes(str, strlen(str)); | |||
os->writeBytes((const uint8_t*)str, strlen(str)); | |||
os->flush(); | |||
} | |||
} | |||
@@ -382,7 +383,7 @@ void SConnection::clientCutText(const char* str) | |||
{ | |||
hasLocalClipboard = false; | |||
clientClipboard = latin1ToUTF8(str); | |||
clientClipboard = str; | |||
hasRemoteClipboard = true; | |||
handleClipboardAnnounce(true); | |||
@@ -428,6 +429,11 @@ void SConnection::handleClipboardProvide(uint32_t flags, | |||
return; | |||
} | |||
// FIXME: This conversion magic should be in SMsgReader | |||
if (!isValidUTF8((const char*)data[0], lengths[0])) { | |||
vlog.error("Invalid UTF-8 sequence in clipboard - ignoring"); | |||
return; | |||
} | |||
clientClipboard = convertLF((const char*)data[0], lengths[0]); | |||
hasRemoteClipboard = true; | |||
@@ -467,7 +473,7 @@ void SConnection::approveConnection(bool accept, const char* reason) | |||
if (!reason) | |||
reason = "Authentication failure"; | |||
os->writeU32(strlen(reason)); | |||
os->writeBytes(reason, strlen(reason)); | |||
os->writeBytes((const uint8_t*)reason, strlen(reason)); | |||
} | |||
} | |||
os->flush(); | |||
@@ -519,7 +525,8 @@ void SConnection::framebufferUpdateRequest(const Rect& /*r*/, | |||
} | |||
} | |||
void SConnection::fence(uint32_t flags, unsigned len, const char data[]) | |||
void SConnection::fence(uint32_t flags, unsigned len, | |||
const uint8_t data[]) | |||
{ | |||
if (!(flags & fenceFlagRequest)) | |||
return; | |||
@@ -590,6 +597,7 @@ void SConnection::sendClipboardData(const char* data) | |||
{ | |||
if (client.supportsEncoding(pseudoEncodingExtendedClipboard) && | |||
(client.clipboardFlags() & rfb::clipboardProvide)) { | |||
// FIXME: This conversion magic should be in SMsgWriter | |||
std::string filtered(convertCRLF(data)); | |||
size_t sizes[1] = { filtered.size() + 1 }; | |||
const uint8_t* data[1] = { (const uint8_t*)filtered.c_str() }; | |||
@@ -606,9 +614,7 @@ void SConnection::sendClipboardData(const char* data) | |||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | |||
} else { | |||
std::string latin1(utf8ToLatin1(data)); | |||
writer()->writeServerCutText(latin1.c_str()); | |||
writer()->writeServerCutText(data); | |||
} | |||
} | |||
@@ -132,7 +132,7 @@ namespace rfb { | |||
// it responds directly to requests (stating it doesn't support any | |||
// synchronisation) and drops responses. Override to implement more proper | |||
// support. | |||
virtual void fence(uint32_t flags, unsigned len, const char data[]); | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
// enableContinuousUpdates() is called when the client wants to enable | |||
// or disable continuous updates, or change the active area. |
@@ -51,7 +51,7 @@ namespace rfb { | |||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental) = 0; | |||
virtual void setDesktopSize(int fb_width, int fb_height, | |||
const ScreenSet& layout) = 0; | |||
virtual void fence(uint32_t flags, unsigned len, const char data[]) = 0; | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]) = 0; | |||
virtual void enableContinuousUpdates(bool enable, | |||
int x, int y, int w, int h) = 0; | |||
@@ -229,7 +229,7 @@ bool SMsgReader::readFence() | |||
{ | |||
uint32_t flags; | |||
uint8_t len; | |||
char data[64]; | |||
uint8_t data[64]; | |||
if (!is->hasData(3 + 4 + 1)) | |||
return false; | |||
@@ -315,8 +315,11 @@ bool SMsgReader::readClientCutText() | |||
} | |||
std::vector<char> ca(len); | |||
is->readBytes(ca.data(), len); | |||
std::string filtered(convertLF(ca.data(), len)); | |||
is->readBytes((uint8_t*)ca.data(), len); | |||
std::string utf8(latin1ToUTF8(ca.data(), ca.size())); | |||
std::string filtered(convertLF(utf8.data(), utf8.size())); | |||
handler->clientCutText(filtered.c_str()); | |||
return true; |
@@ -38,6 +38,7 @@ | |||
#include <rfb/SMsgWriter.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/ledStates.h> | |||
#include <rfb/util.h> | |||
using namespace rfb; | |||
@@ -63,7 +64,7 @@ void SMsgWriter::writeServerInit(uint16_t width, uint16_t height, | |||
os->writeU16(height); | |||
pf.write(os); | |||
os->writeU32(strlen(name)); | |||
os->writeBytes(name, strlen(name)); | |||
os->writeBytes((const uint8_t*)name, strlen(name)); | |||
endMsg(); | |||
} | |||
@@ -92,16 +93,15 @@ void SMsgWriter::writeBell() | |||
void SMsgWriter::writeServerCutText(const char* str) | |||
{ | |||
size_t len; | |||
if (strchr(str, '\r') != NULL) | |||
throw Exception("Invalid carriage return in clipboard data"); | |||
len = strlen(str); | |||
std::string latin1(utf8ToLatin1(str)); | |||
startMsg(msgTypeServerCutText); | |||
os->pad(3); | |||
os->writeU32(len); | |||
os->writeBytes(str, len); | |||
os->writeU32(latin1.size()); | |||
os->writeBytes((const uint8_t*)latin1.data(), latin1.size()); | |||
endMsg(); | |||
} | |||
@@ -211,7 +211,8 @@ void SMsgWriter::writeClipboardProvide(uint32_t flags, | |||
endMsg(); | |||
} | |||
void SMsgWriter::writeFence(uint32_t flags, unsigned len, const char data[]) | |||
void SMsgWriter::writeFence(uint32_t flags, unsigned len, | |||
const uint8_t data[]) | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingFence)) | |||
throw Exception("Client does not support fences"); | |||
@@ -585,12 +586,13 @@ void SMsgWriter::writeSetDesktopNameRect(const char *name) | |||
os->writeU16(0); | |||
os->writeU32(pseudoEncodingDesktopName); | |||
os->writeU32(strlen(name)); | |||
os->writeBytes(name, strlen(name)); | |||
os->writeBytes((const uint8_t*)name, strlen(name)); | |||
} | |||
void SMsgWriter::writeSetCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask) | |||
const uint8_t* data, | |||
const uint8_t* mask) | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingCursor)) | |||
throw Exception("Client does not support local cursors"); | |||
@@ -608,7 +610,8 @@ void SMsgWriter::writeSetCursorRect(int width, int height, | |||
void SMsgWriter::writeSetXCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask) | |||
const uint8_t* data, | |||
const uint8_t* mask) | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingXCursor)) | |||
throw Exception("Client does not support local cursors"); |
@@ -68,7 +68,7 @@ namespace rfb { | |||
const uint8_t* const* data); | |||
// writeFence() sends a new fence request or response to the client. | |||
void writeFence(uint32_t flags, unsigned len, const char data[]); | |||
void writeFence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
// writeEndOfContinuousUpdates() indicates that we have left continuous | |||
// updates mode. | |||
@@ -135,10 +135,10 @@ namespace rfb { | |||
void writeSetDesktopNameRect(const char *name); | |||
void writeSetCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask); | |||
const uint8_t* data, const uint8_t* mask); | |||
void writeSetXCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask); | |||
const uint8_t* data, const uint8_t* mask); | |||
void writeSetCursorWithAlphaRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const uint8_t* data); |
@@ -99,8 +99,8 @@ bool SSecurityPlain::processMsg() | |||
if (!is->hasData(ulen + plen)) | |||
return false; | |||
state = 2; | |||
is->readBytes(username, ulen); | |||
is->readBytes(password, plen); | |||
is->readBytes((uint8_t*)username, ulen); | |||
is->readBytes((uint8_t*)password, plen); | |||
password[plen] = 0; | |||
username[ulen] = 0; | |||
plen = 0; |
@@ -539,12 +539,12 @@ bool SSecurityRSAAES::readCredentials() | |||
uint8_t lenUsername = rais->readU8(); | |||
if (!rais->hasDataOrRestore(lenUsername + 1)) | |||
return false; | |||
rais->readBytes(username, lenUsername); | |||
rais->readBytes((uint8_t*)username, lenUsername); | |||
username[lenUsername] = 0; | |||
uint8_t lenPassword = rais->readU8(); | |||
if (!rais->hasDataOrRestore(lenPassword)) | |||
return false; | |||
rais->readBytes(password, lenPassword); | |||
rais->readBytes((uint8_t*)password, lenPassword); | |||
password[lenPassword] = 0; | |||
rais->clearRestorePoint(); | |||
return true; |
@@ -192,10 +192,10 @@ bool TightDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
} | |||
bool TightDecoder::doRectsConflict(const Rect& /*rectA*/, | |||
const void* bufferA, | |||
const uint8_t* bufferA, | |||
size_t buflenA, | |||
const Rect& /*rectB*/, | |||
const void* bufferB, | |||
const uint8_t* bufferB, | |||
size_t buflenB, | |||
const ServerParams& /*server*/) | |||
{ | |||
@@ -219,7 +219,7 @@ bool TightDecoder::doRectsConflict(const Rect& /*rectA*/, | |||
return false; | |||
} | |||
void TightDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void TightDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ |
@@ -34,13 +34,13 @@ namespace rfb { | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual bool doRectsConflict(const Rect& rectA, | |||
const void* bufferA, | |||
const uint8_t* bufferA, | |||
size_t buflenA, | |||
const Rect& rectB, | |||
const void* bufferB, | |||
const uint8_t* bufferB, | |||
size_t buflenB, | |||
const ServerParams& server); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
@@ -672,7 +672,7 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height, | |||
writer()->writeDesktopSize(reasonClient, result); | |||
} | |||
void VNCSConnectionST::fence(uint32_t flags, unsigned len, const char data[]) | |||
void VNCSConnectionST::fence(uint32_t flags, unsigned len, const uint8_t data[]) | |||
{ | |||
uint8_t type; | |||
@@ -685,7 +685,7 @@ void VNCSConnectionST::fence(uint32_t flags, unsigned len, const char data[]) | |||
delete [] fenceData; | |||
fenceData = NULL; | |||
if (len > 0) { | |||
fenceData = new char[len]; | |||
fenceData = new uint8_t[len]; | |||
memcpy(fenceData, data, len); | |||
} | |||
@@ -771,7 +771,7 @@ void VNCSConnectionST::supportsLocalCursor() | |||
void VNCSConnectionST::supportsFence() | |||
{ | |||
char type = 0; | |||
uint8_t type = 0; | |||
writer()->writeFence(fenceFlagRequest, sizeof(type), &type); | |||
} | |||
@@ -825,7 +825,7 @@ bool VNCSConnectionST::isShiftPressed() | |||
void VNCSConnectionST::writeRTTPing() | |||
{ | |||
char type; | |||
uint8_t type; | |||
if (!client.supportsFence()) | |||
return; |
@@ -128,7 +128,7 @@ namespace rfb { | |||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental); | |||
virtual void setDesktopSize(int fb_width, int fb_height, | |||
const ScreenSet& layout); | |||
virtual void fence(uint32_t flags, unsigned len, const char data[]); | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
virtual void enableContinuousUpdates(bool enable, | |||
int x, int y, int w, int h); | |||
virtual void handleClipboardRequest(); | |||
@@ -174,7 +174,7 @@ namespace rfb { | |||
bool pendingSyncFence, syncFence; | |||
uint32_t fenceFlags; | |||
unsigned fenceDataLen; | |||
char *fenceData; | |||
uint8_t *fenceData; | |||
Congestion congestion; | |||
Timer congestionTimer; |
@@ -139,11 +139,11 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) | |||
rdr::OutStream& os = sock->outStream(); | |||
// Shortest possible way to tell a client it is not welcome | |||
os.writeBytes("RFB 003.003\n", 12); | |||
os.writeBytes((const uint8_t*)"RFB 003.003\n", 12); | |||
os.writeU32(0); | |||
const char* reason = "Too many security failures"; | |||
os.writeU32(strlen(reason)); | |||
os.writeBytes(reason, strlen(reason)); | |||
os.writeBytes((const uint8_t*)reason, strlen(reason)); | |||
os.flush(); | |||
} catch (rdr::Exception&) { | |||
} |
@@ -99,7 +99,7 @@ bool ZRLEDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, | |||
return true; | |||
} | |||
void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer, | |||
void ZRLEDecoder::decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
@@ -185,7 +185,7 @@ void ZRLEDecoder::zrleDecode(const Rect& r, rdr::InStream* is, | |||
*ptr = readOpaque24B(zis); | |||
} | |||
} else { | |||
zis->readBytes(buf, t.area() * sizeof(T)); | |||
zis->readBytes((uint8_t*)buf, t.area() * sizeof(T)); | |||
} | |||
} else { |
@@ -32,7 +32,7 @@ namespace rfb { | |||
virtual ~ZRLEDecoder(); | |||
virtual bool readRect(const Rect& r, rdr::InStream* is, | |||
const ServerParams& server, rdr::OutStream* os); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
virtual void decodeRect(const Rect& r, const uint8_t* buffer, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
@@ -266,6 +266,8 @@ namespace rfb { | |||
*dst++ = 0x80 | (src & 0x3f); | |||
*dst++ = '\0'; | |||
return 2; | |||
} else if ((src >= 0xd800) && (src < 0xe000)) { | |||
return ucs4ToUTF8(0xfffd, dst); | |||
} else if (src < 0x10000) { | |||
*dst++ = 0xe0 | (src >> 12); | |||
*dst++ = 0x80 | ((src >> 6) & 0x3f); | |||
@@ -334,6 +336,10 @@ namespace rfb { | |||
max--; | |||
} | |||
// UTF-16 surrogate code point? | |||
if ((*dst >= 0xd800) && (*dst < 0xe000)) | |||
*dst = 0xfffd; | |||
return consumed; | |||
} | |||
@@ -564,6 +570,40 @@ namespace rfb { | |||
return out; | |||
} | |||
bool isValidUTF8(const char* str, size_t bytes) | |||
{ | |||
while ((bytes > 0) && (*str != '\0')) { | |||
size_t len; | |||
unsigned ucs; | |||
len = utf8ToUCS4(str, bytes, &ucs); | |||
str += len; | |||
bytes -= len; | |||
if (ucs == 0xfffd) | |||
return false; | |||
} | |||
return true; | |||
} | |||
bool isValidUTF16(const wchar_t* wstr, size_t units) | |||
{ | |||
while ((units > 0) && (*wstr != '\0')) { | |||
size_t len; | |||
unsigned ucs; | |||
len = utf16ToUCS4(wstr, units, &ucs); | |||
wstr += len; | |||
units -= len; | |||
if (ucs == 0xfffd) | |||
return false; | |||
} | |||
return true; | |||
} | |||
unsigned msBetween(const struct timeval *first, | |||
const struct timeval *second) | |||
{ |
@@ -68,6 +68,9 @@ namespace rfb { | |||
std::string utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1); | |||
std::wstring utf8ToUTF16(const char* src, size_t bytes = (size_t)-1); | |||
bool isValidUTF8(const char* str, size_t bytes = (size_t)-1); | |||
bool isValidUTF16(const wchar_t* wstr, size_t units = (size_t)-1); | |||
// HELPER functions for timeout handling | |||
// soonestTimeout() is a function to help work out the soonest of several |
@@ -53,6 +53,10 @@ struct _ucs4utf8 ucs4utf8[] = { | |||
{ 0x1f638, "\xf0\x9f\x98\xb8" }, | |||
{ 0x2d006, "\xf0\xad\x80\x86" }, | |||
{ 0xfffd, "\xe5\xe4" }, | |||
{ 0xfffd, "\xed\xa2\x80" }, | |||
{ 0xfffd, "\xed\xbb\xbf" }, | |||
{ 0xd880, "\xef\xbf\xbd" }, | |||
{ 0xdeff, "\xef\xbf\xbd" }, | |||
{ 0x110200, "\xef\xbf\xbd" }, | |||
}; | |||
@@ -84,6 +88,28 @@ struct _utf8utf16 utf8utf16[] = { | |||
{ "\xed\xa1\xbf", L"\xfffd" }, | |||
}; | |||
const char *validutf8[] = { | |||
"abc", | |||
"\xc3\xa5\xc3\xa4\xc3\xb6", | |||
"\xf0\xad\x80\x86", | |||
}; | |||
const char *invalidutf8[] = { | |||
"\xe5\xe4\xf6", | |||
"\xf8\xa1\xa1\xa1\xa1", | |||
"\xed\xa2\x80", | |||
}; | |||
const wchar_t *validutf16[] = { | |||
L"abc", | |||
L"\xe5\xe4\xf6", | |||
L"\xd83d\xde38\xd83d\xde41\xd83d\xde42", | |||
}; | |||
const wchar_t *invalidutf16[] = { | |||
L"\xdc40\xdc12", | |||
}; | |||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*a)) | |||
int main(int /*argc*/, char** /*argv*/) | |||
@@ -196,6 +222,34 @@ int main(int /*argc*/, char** /*argv*/) | |||
} | |||
} | |||
for (i = 0;i < ARRAY_SIZE(validutf8);i++) { | |||
if (!rfb::isValidUTF8(validutf8[i])) { | |||
printf("FAILED: isValidUTF8() #%d\n", (int)i+1); | |||
failures++; | |||
} | |||
} | |||
for (i = 0;i < ARRAY_SIZE(invalidutf8);i++) { | |||
if (rfb::isValidUTF8(invalidutf8[i])) { | |||
printf("FAILED: ! isValidUTF8() #%d\n", (int)i+1); | |||
failures++; | |||
} | |||
} | |||
for (i = 0;i < ARRAY_SIZE(validutf16);i++) { | |||
if (!rfb::isValidUTF16(validutf16[i])) { | |||
printf("FAILED: isValidUTF16() #%d\n", (int)i+1); | |||
failures++; | |||
} | |||
} | |||
for (i = 0;i < ARRAY_SIZE(invalidutf16);i++) { | |||
if (rfb::isValidUTF16(invalidutf16[i])) { | |||
printf("FAILED: ! isValidUTF16() #%d\n", (int)i+1); | |||
failures++; | |||
} | |||
} | |||
if (failures == 0) { | |||
printf("OK\n"); | |||
} else { |
@@ -248,3 +248,12 @@ char* vncUTF8ToLatin1(const char* src, size_t bytes) | |||
return NULL; | |||
} | |||
} | |||
int vncIsValidUTF8(const char* str, size_t bytes) | |||
{ | |||
try { | |||
return isValidUTF8(str, bytes); | |||
} catch (...) { | |||
return 0; | |||
} | |||
} |
@@ -53,6 +53,8 @@ char* vncConvertLF(const char* src, size_t bytes); | |||
char* vncLatin1ToUTF8(const char* src, size_t bytes); | |||
char* vncUTF8ToLatin1(const char* src, size_t bytes); | |||
int vncIsValidUTF8(const char* str, size_t bytes); | |||
#ifdef __cplusplus | |||
} | |||
#endif |
@@ -611,6 +611,11 @@ static void vncHandleSelection(Atom selection, Atom target, | |||
if (prop->type != xaUTF8_STRING) | |||
return; | |||
if (!vncIsValidUTF8(prop->data, prop->size)) { | |||
LOG_ERROR("Invalid UTF-8 sequence in clipboard"); | |||
return; | |||
} | |||
filtered = vncConvertLF(prop->data, prop->size); | |||
if (filtered == NULL) | |||
return; |
@@ -432,7 +432,7 @@ void CConn::setCursorPos(const Point& pos) | |||
desktop->setCursorPos(pos); | |||
} | |||
void CConn::fence(uint32_t flags, unsigned len, const char data[]) | |||
void CConn::fence(uint32_t flags, unsigned len, const uint8_t data[]) | |||
{ | |||
CMsgHandler::fence(flags, len, data); | |||
@@ -65,7 +65,7 @@ public: | |||
const uint8_t* data); | |||
void setCursorPos(const rfb::Point& pos); | |||
void fence(uint32_t flags, unsigned len, const char data[]); | |||
void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
void setLEDState(unsigned int state); | |||
@@ -566,6 +566,11 @@ int Viewport::handle(int event) | |||
switch (event) { | |||
case FL_PASTE: | |||
if (!isValidUTF8(Fl::event_text(), Fl::event_length())) { | |||
vlog.error("Invalid UTF-8 sequence in system clipboard"); | |||
return 1; | |||
} | |||
filtered = convertLF(Fl::event_text(), Fl::event_length()); | |||
vlog.debug("Sending clipboard data (%d bytes)", (int)filtered.size()); |