This is required by the protocol so we should make sure it is enforced. We are tolerant of clients that violate this though and convert incoming clipboard data.tags/v1.9.90
@@ -157,10 +157,10 @@ void CMsgReader::readServerCutText() | |||
vlog.error("cut text too long (%d bytes) - ignoring",len); | |||
return; | |||
} | |||
CharArray ca(len+1); | |||
ca.buf[len] = 0; | |||
CharArray ca(len); | |||
is->readBytes(ca.buf, len); | |||
handler->serverCutText(ca.buf, len); | |||
CharArray filtered(convertLF(ca.buf, len)); | |||
handler->serverCutText(filtered.buf, strlen(filtered.buf)); | |||
} | |||
void CMsgReader::readFence() |
@@ -181,6 +181,9 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) | |||
void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len) | |||
{ | |||
if (memchr(str, '\r', len) != NULL) | |||
throw Exception("Invalid carriage return in clipboard data"); | |||
startMsg(msgTypeClientCutText); | |||
os->pad(3); | |||
os->writeU32(len); |
@@ -212,10 +212,10 @@ void SMsgReader::readClientCutText() | |||
vlog.error("Cut text too long (%d bytes) - ignoring", len); | |||
return; | |||
} | |||
CharArray ca(len+1); | |||
ca.buf[len] = 0; | |||
CharArray ca(len); | |||
is->readBytes(ca.buf, len); | |||
handler->clientCutText(ca.buf, len); | |||
CharArray filtered(convertLF(ca.buf, len)); | |||
handler->clientCutText(filtered.buf, strlen(filtered.buf)); | |||
} | |||
void SMsgReader::readQEMUMessage() |
@@ -80,6 +80,9 @@ void SMsgWriter::writeBell() | |||
void SMsgWriter::writeServerCutText(const char* str, int len) | |||
{ | |||
if (memchr(str, '\r', len) != NULL) | |||
throw Exception("Invalid carriage return in clipboard data"); | |||
startMsg(msgTypeServerCutText); | |||
os->pad(3); | |||
os->writeU32(len); |
@@ -342,6 +342,8 @@ void VNCServerST::bell() | |||
void VNCServerST::serverCutText(const char* str, int len) | |||
{ | |||
if (memchr(str, '\r', len) != NULL) | |||
throw Exception("Invalid carriage return in clipboard data"); | |||
std::list<VNCSConnectionST*>::iterator ci, ci_next; | |||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { | |||
ci_next = ci; ci_next++; |
@@ -1,4 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -107,6 +108,61 @@ namespace rfb { | |||
dest[src ? destlen-1 : 0] = 0; | |||
} | |||
char* convertLF(const char* src, size_t bytes) | |||
{ | |||
char* buffer; | |||
size_t sz; | |||
char* out; | |||
const char* in; | |||
size_t in_len; | |||
// Always include space for a NULL | |||
sz = 1; | |||
// Compute output size | |||
in = src; | |||
in_len = bytes; | |||
while ((*in != '\0') && (in_len > 0)) { | |||
if (*in != '\r') { | |||
sz++; | |||
in++; | |||
in_len--; | |||
continue; | |||
} | |||
if ((in_len == 0) || (*(in+1) != '\n')) | |||
sz++; | |||
in++; | |||
in_len--; | |||
} | |||
// Alloc | |||
buffer = new char[sz]; | |||
memset(buffer, 0, sz); | |||
// And convert | |||
out = buffer; | |||
in = src; | |||
in_len = bytes; | |||
while ((*in != '\0') && (in_len > 0)) { | |||
if (*in != '\r') { | |||
*out++ = *in++; | |||
in_len--; | |||
continue; | |||
} | |||
if ((in_len == 0) || (*(in+1) != '\n')) | |||
*out++ = '\n'; | |||
in++; | |||
in_len--; | |||
} | |||
return buffer; | |||
} | |||
unsigned msBetween(const struct timeval *first, | |||
const struct timeval *second) | |||
{ |
@@ -1,4 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -83,6 +84,9 @@ namespace rfb { | |||
// Copies src to dest, up to specified length-1, and guarantees termination | |||
void strCopy(char* dest, const char* src, int destlen); | |||
// Makes sure line endings are in a certain format | |||
char* convertLF(const char* src, size_t bytes = (size_t)-1); | |||
// HELPER functions for timeout handling | |||
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011-2015 Pierre Ossman for Cendio AB | |||
* Copyright 2011-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -210,3 +210,17 @@ int vncIsTCPPortUsed(int port) | |||
} | |||
return 0; | |||
} | |||
char* vncConvertLF(const char* src, size_t bytes) | |||
{ | |||
try { | |||
return convertLF(src, bytes); | |||
} catch (...) { | |||
return NULL; | |||
} | |||
} | |||
void vncStrFree(char* str) | |||
{ | |||
strFree(str); | |||
} |
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011-2015 Pierre Ossman for Cendio AB | |||
* Copyright 2011-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -49,6 +49,9 @@ void vncListParams(int width, int nameWidth); | |||
int vncGetSocketPort(int fd); | |||
int vncIsTCPPortUsed(int port); | |||
char* vncConvertLF(const char* src, size_t bytes); | |||
void vncStrFree(char* str); | |||
#ifdef __cplusplus | |||
} | |||
#endif |
@@ -1,4 +1,4 @@ | |||
/* Copyright 2016 Pierre Ossman for Cendio AB | |||
/* Copyright 2016-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -415,13 +415,22 @@ static void vncHandleSelection(Atom selection, Atom target, | |||
else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) | |||
vncSelectionRequest(selection, xaUTF8_STRING); | |||
} else if (target == xaSTRING) { | |||
char* filtered; | |||
if (prop->format != 8) | |||
return; | |||
if (prop->type != xaSTRING) | |||
return; | |||
vncServerCutText(prop->data, prop->size); | |||
filtered = vncConvertLF(prop->data, prop->size); | |||
if (filtered == NULL) | |||
return; | |||
vncServerCutText(filtered, strlen(filtered)); | |||
vncStrFree(filtered); | |||
} else if (target == xaUTF8_STRING) { | |||
char *filtered; | |||
unsigned char* buffer; | |||
unsigned char* out; | |||
size_t len; | |||
@@ -470,9 +479,14 @@ static void vncHandleSelection(Atom selection, Atom target, | |||
} | |||
} | |||
vncServerCutText((const char*)buffer, len); | |||
filtered = vncConvertLF(buffer, len); | |||
free(buffer); | |||
if (filtered == NULL) | |||
return; | |||
vncServerCutText(filtered, strlen(filtered)); | |||
vncStrFree(filtered); | |||
} | |||
} | |||
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011-2014 Pierre Ossman for Cendio AB | |||
* Copyright 2011-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -549,7 +549,7 @@ void Viewport::resize(int x, int y, int w, int h) | |||
int Viewport::handle(int event) | |||
{ | |||
char *buffer; | |||
char *buffer, *filtered; | |||
int ret; | |||
int buttonMask, wheelMask; | |||
DownMap::const_iterator iter; | |||
@@ -564,22 +564,26 @@ int Viewport::handle(int event) | |||
ret = fl_utf8toa(Fl::event_text(), Fl::event_length(), buffer, | |||
Fl::event_length() + 1); | |||
assert(ret < (Fl::event_length() + 1)); | |||
filtered = convertLF(buffer, ret); | |||
delete [] buffer; | |||
if (!hasFocus()) { | |||
pendingClientCutText = buffer; | |||
pendingClientCutText = new char[strlen(filtered) + 1]; | |||
strcpy((char*)pendingClientCutText, filtered); | |||
strFree(filtered); | |||
return 1; | |||
} | |||
vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(buffer)); | |||
vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); | |||
try { | |||
cc->writer()->writeClientCutText(buffer, ret); | |||
cc->writer()->writeClientCutText(filtered, strlen(filtered)); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
} | |||
delete [] buffer; | |||
strFree(filtered); | |||
return 1; | |||
@@ -1,4 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2012-2019 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -34,18 +35,6 @@ static LogWriter vlog("Clipboard"); | |||
// -=- CR/LF handlers | |||
// | |||
char* | |||
dos2unix(const char* text) { | |||
int len = strlen(text)+1; | |||
char* unix = new char[strlen(text)+1]; | |||
int i, j=0; | |||
for (i=0; i<len; i++) { | |||
if (text[i] != '\x0d') | |||
unix[j++] = text[i]; | |||
} | |||
return unix; | |||
} | |||
char* | |||
unix2dos(const char* text) { | |||
int len = strlen(text)+1; | |||
@@ -126,8 +115,7 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { | |||
if (!clipdata) { | |||
notifier->notifyClipboardChanged(0, 0); | |||
} else { | |||
CharArray unix_text; | |||
unix_text.buf = dos2unix(clipdata); | |||
CharArray unix_text(convertLF(clipdata, strlen(clipdata))); | |||
removeNonISOLatin1Chars(unix_text.buf); | |||
notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); | |||
} |