@@ -151,8 +151,6 @@ namespace network { | |||
// there is no timeout and checkTimeouts() should be called the next time | |||
// an event occurs. | |||
virtual int checkTimeouts() = 0; | |||
virtual bool getDisable() {return false;}; | |||
}; | |||
} |
@@ -260,13 +260,14 @@ void SConnection::throwConnFailedException(const char* format, ...) | |||
throw ConnFailedException(str); | |||
} | |||
void SConnection::writeConnFailedFromScratch(const char* msg, | |||
rdr::OutStream* os) | |||
void SConnection::setAccessRights(AccessRights ar) | |||
{ | |||
os->writeBytes("RFB 003.003\n", 12); | |||
os->writeU32(0); | |||
os->writeString(msg); | |||
os->flush(); | |||
accessRights = ar; | |||
} | |||
bool SConnection::accessCheck(AccessRights ar) const | |||
{ | |||
return (accessRights & ar) == ar; | |||
} | |||
void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
@@ -342,6 +343,11 @@ void SConnection::clientInit(bool shared) | |||
state_ = RFBSTATE_NORMAL; | |||
} | |||
void SConnection::close(const char* reason) | |||
{ | |||
state_ = RFBSTATE_CLOSING; | |||
} | |||
void SConnection::setPixelFormat(const PixelFormat& pf) | |||
{ | |||
SMsgHandler::setPixelFormat(pf); |
@@ -69,6 +69,13 @@ namespace rfb { | |||
void approveConnection(bool accept, const char* reason=0); | |||
// Methods to terminate the connection | |||
// close() shuts down the connection to the client and awaits | |||
// cleanup of the SConnection object by the server | |||
virtual void close(const char* reason); | |||
// Overridden from SMsgHandler | |||
virtual void setEncodings(int nEncodings, const rdr::S32* encodings); | |||
@@ -118,8 +125,10 @@ namespace rfb { | |||
virtual void enableContinuousUpdates(bool enable, | |||
int x, int y, int w, int h); | |||
// Other methods | |||
// setAccessRights() allows a security package to limit the access rights | |||
// of a VNCSConnectionST to the server. How the access rights are treated | |||
// of a SConnection to the server. How the access rights are treated | |||
// is up to the derived class. | |||
typedef rdr::U16 AccessRights; | |||
@@ -132,27 +141,14 @@ namespace rfb { | |||
static const AccessRights AccessDefault; // The default rights, INCLUDING FUTURE ONES | |||
static const AccessRights AccessNoQuery; // Connect without local user accepting | |||
static const AccessRights AccessFull; // All of the available AND FUTURE rights | |||
virtual void setAccessRights(AccessRights ar) = 0; | |||
// Other methods | |||
virtual void setAccessRights(AccessRights ar); | |||
virtual bool accessCheck(AccessRights ar) const; | |||
// authenticated() returns true if the client has authenticated | |||
// successfully. | |||
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || | |||
state_ == RFBSTATE_NORMAL); } | |||
// throwConnFailedException() prints a message to the log, sends a conn | |||
// failed message to the client (if possible) and throws a | |||
// ConnFailedException. | |||
void throwConnFailedException(const char* format, ...) __printf_attr(2, 3); | |||
// writeConnFailedFromScratch() sends a conn failed message to an OutStream | |||
// without the need to negotiate the protocol version first. It actually | |||
// does this by assuming that the client will understand version 3.3 of the | |||
// protocol. | |||
static void writeConnFailedFromScratch(const char* msg, | |||
rdr::OutStream* os); | |||
SMsgReader* reader() { return reader_; } | |||
SMsgWriter* writer() { return writer_; } | |||
@@ -176,6 +172,11 @@ namespace rfb { | |||
rdr::S32 getPreferredEncoding() { return preferredEncoding; } | |||
protected: | |||
// throwConnFailedException() prints a message to the log, sends a conn | |||
// failed message to the client (if possible) and throws a | |||
// ConnFailedException. | |||
void throwConnFailedException(const char* format, ...) __printf_attr(2, 3); | |||
void setState(stateEnum s) { state_ = s; } | |||
void setReader(SMsgReader *r) { reader_ = r; } | |||
@@ -201,6 +202,7 @@ namespace rfb { | |||
SSecurity* ssecurity; | |||
stateEnum state_; | |||
rdr::S32 preferredEncoding; | |||
AccessRights accessRights; | |||
}; | |||
} | |||
#endif |
@@ -44,6 +44,8 @@ | |||
#include <rfb/screenTypes.h> | |||
#include <rfb/util.h> | |||
namespace network { class Socket; } | |||
namespace rfb { | |||
class VNCServer; | |||
@@ -56,14 +58,22 @@ namespace rfb { | |||
// set via the VNCServer's setPixelBuffer() method by the time this call | |||
// returns. | |||
virtual void start(VNCServer* __unused_attr vs) {} | |||
virtual void start(VNCServer* vs) = 0; | |||
// stop() is called by the server when there are no longer any | |||
// authenticated clients, and therefore the desktop can cease any | |||
// expensive tasks. No further calls to the VNCServer passed to start() | |||
// can be made once stop has returned. | |||
virtual void stop() {} | |||
virtual void stop() = 0; | |||
// queryConnection() is called when a connection has been | |||
// successfully authenticated. The sock and userName arguments | |||
// identify the socket and the name of the authenticated user, if | |||
// any. At some point later VNCServer::approveConnection() should | |||
// be called to either accept or reject the client. | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName) = 0; | |||
// setScreenLayout() requests to reconfigure the framebuffer and/or | |||
// the layout of screens. | |||
@@ -112,6 +122,10 @@ namespace rfb { | |||
server->setPixelBuffer(0); | |||
server = 0; | |||
} | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName) { | |||
server->approveConnection(sock, true, NULL); | |||
} | |||
protected: | |||
VNCServer* server; |
@@ -52,27 +52,22 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | |||
losslessTimer(this), server(server_), updates(false), | |||
updateRenderedCursor(false), removeRenderedCursor(false), | |||
continuousUpdates(false), encodeManager(this), pointerEventTime(0), | |||
clientHasCursor(false), | |||
accessRights(AccessDefault), startTime(time(0)) | |||
clientHasCursor(false) | |||
{ | |||
setStreams(&sock->inStream(), &sock->outStream()); | |||
peerEndpoint.buf = sock->getPeerEndpoint(); | |||
VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf); | |||
// Configure the socket | |||
setSocketTimeouts(); | |||
lastEventTime = time(0); | |||
server->clients.push_front(this); | |||
} | |||
VNCSConnectionST::~VNCSConnectionST() | |||
{ | |||
// If we reach here then VNCServerST is deleting us! | |||
VNCServerST::connectionsLog.write(1,"closed: %s (%s)", | |||
peerEndpoint.buf, | |||
(closeReason.buf) ? closeReason.buf : ""); | |||
if (closeReason.buf) | |||
vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf); | |||
// Release any keys the client still had pressed | |||
while (!pressedKeys.empty()) { | |||
@@ -84,30 +79,23 @@ VNCSConnectionST::~VNCSConnectionST() | |||
vlog.debug("Releasing key 0x%x / 0x%x on client disconnect", | |||
keysym, keycode); | |||
server->desktop->keyEvent(keysym, keycode, false); | |||
server->keyEvent(keysym, keycode, false); | |||
} | |||
if (server->pointerClient == this) | |||
server->pointerClient = 0; | |||
// Remove this client from the server | |||
server->clients.remove(this); | |||
delete [] fenceData; | |||
} | |||
// Methods called from VNCServerST | |||
// SConnection methods | |||
bool VNCSConnectionST::init() | |||
bool VNCSConnectionST::accessCheck(AccessRights ar) const | |||
{ | |||
try { | |||
initialiseProtocol(); | |||
} catch (rdr::Exception& e) { | |||
close(e.str()); | |||
return false; | |||
} | |||
return true; | |||
// Reverse connections are user initiated, so they are implicitly | |||
// allowed to bypass the query | |||
if (reverseConnection) | |||
ar &= ~AccessNoQuery; | |||
return SConnection::accessCheck(ar); | |||
} | |||
void VNCSConnectionST::close(const char* reason) | |||
@@ -118,15 +106,26 @@ void VNCSConnectionST::close(const char* reason) | |||
else | |||
vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason); | |||
if (authenticated()) { | |||
server->lastDisconnectTime = time(0); | |||
} | |||
// Just shutdown the socket and mark our state as closing. Eventually the | |||
// calling code will call VNCServerST's removeSocket() method causing us to | |||
// be deleted. | |||
sock->shutdown(); | |||
setState(RFBSTATE_CLOSING); | |||
SConnection::close(reason); | |||
} | |||
// Methods called from VNCServerST | |||
bool VNCSConnectionST::init() | |||
{ | |||
try { | |||
initialiseProtocol(); | |||
} catch (rdr::Exception& e) { | |||
close(e.str()); | |||
return false; | |||
} | |||
return true; | |||
} | |||
@@ -192,8 +191,9 @@ void VNCSConnectionST::pixelBufferChange() | |||
{ | |||
try { | |||
if (!authenticated()) return; | |||
if (cp.width && cp.height && (server->pb->width() != cp.width || | |||
server->pb->height() != cp.height)) | |||
if (cp.width && cp.height && | |||
(server->getPixelBuffer()->width() != cp.width || | |||
server->getPixelBuffer()->height() != cp.height)) | |||
{ | |||
// We need to clip the next update to the new size, but also add any | |||
// extra bits if it's bigger. If we wanted to do this exactly, something | |||
@@ -210,11 +210,11 @@ void VNCSConnectionST::pixelBufferChange() | |||
// updates.add_changed(Rect(0, cp.height, cp.width, | |||
// server->pb->height())); | |||
damagedCursorRegion.assign_intersect(server->pb->getRect()); | |||
damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect()); | |||
cp.width = server->pb->width(); | |||
cp.height = server->pb->height(); | |||
cp.screenLayout = server->screenLayout; | |||
cp.width = server->getPixelBuffer()->width(); | |||
cp.height = server->getPixelBuffer()->height(); | |||
cp.screenLayout = server->getScreenLayout(); | |||
if (state() == RFBSTATE_NORMAL) { | |||
// We should only send EDS to client asking for both | |||
if (!writer()->writeExtendedDesktopSize()) { | |||
@@ -226,12 +226,12 @@ void VNCSConnectionST::pixelBufferChange() | |||
} | |||
// Drop any lossy tracking that is now outside the framebuffer | |||
encodeManager.pruneLosslessRefresh(Region(server->pb->getRect())); | |||
encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect())); | |||
} | |||
// Just update the whole screen at the moment because we're too lazy to | |||
// work out what's actually changed. | |||
updates.clear(); | |||
updates.add_changed(server->pb->getRect()); | |||
updates.add_changed(server->getPixelBuffer()->getRect()); | |||
writeFramebufferUpdate(); | |||
} catch(rdr::Exception &e) { | |||
close(e.str()); | |||
@@ -269,7 +269,7 @@ void VNCSConnectionST::bellOrClose() | |||
void VNCSConnectionST::serverCutTextOrClose(const char *str, int len) | |||
{ | |||
try { | |||
if (!(accessRights & AccessCutText)) return; | |||
if (!accessCheck(AccessCutText)) return; | |||
if (!rfb::Server::sendCutText) return; | |||
if (state() == RFBSTATE_NORMAL) | |||
writer()->writeServerCutText(str, len); | |||
@@ -388,7 +388,7 @@ bool VNCSConnectionST::needRenderedCursor() | |||
if (!cp.supportsLocalCursorWithAlpha && | |||
!cp.supportsLocalCursor && !cp.supportsLocalXCursor) | |||
return true; | |||
if (!server->cursorPos.equals(pointerEventPos) && | |||
if (!server->getCursorPos().equals(pointerEventPos) && | |||
(time(0) - pointerEventTime) > 0) | |||
return true; | |||
@@ -414,82 +414,36 @@ void VNCSConnectionST::authSuccess() | |||
{ | |||
lastEventTime = time(0); | |||
server->startDesktop(); | |||
// - Set the connection parameters appropriately | |||
cp.width = server->pb->width(); | |||
cp.height = server->pb->height(); | |||
cp.screenLayout = server->screenLayout; | |||
cp.width = server->getPixelBuffer()->width(); | |||
cp.height = server->getPixelBuffer()->height(); | |||
cp.screenLayout = server->getScreenLayout(); | |||
cp.setName(server->getName()); | |||
cp.setLEDState(server->ledState); | |||
cp.setLEDState(server->getLEDState()); | |||
// - Set the default pixel format | |||
cp.setPF(server->pb->getPF()); | |||
cp.setPF(server->getPixelBuffer()->getPF()); | |||
char buffer[256]; | |||
cp.pf().print(buffer, 256); | |||
vlog.info("Server default pixel format %s", buffer); | |||
// - Mark the entire display as "dirty" | |||
updates.add_changed(server->pb->getRect()); | |||
startTime = time(0); | |||
updates.add_changed(server->getPixelBuffer()->getRect()); | |||
} | |||
void VNCSConnectionST::queryConnection(const char* userName) | |||
{ | |||
// - Authentication succeeded - clear from blacklist | |||
CharArray name; name.buf = sock->getPeerAddress(); | |||
server->blHosts->clearBlackmark(name.buf); | |||
// - Special case to provide a more useful error message | |||
if (rfb::Server::neverShared && !rfb::Server::disconnectClients && | |||
server->authClientCount() > 0) { | |||
approveConnection(false, "The server is already in use"); | |||
return; | |||
} | |||
// - Does the client have the right to bypass the query? | |||
if (reverseConnection || | |||
!(rfb::Server::queryConnect || sock->requiresQuery()) || | |||
(accessRights & AccessNoQuery)) | |||
{ | |||
approveConnection(true); | |||
return; | |||
} | |||
// - Get the server to display an Accept/Reject dialog, if required | |||
// If a dialog is displayed, the result will be PENDING, and the | |||
// server will call approveConnection at a later time | |||
CharArray reason; | |||
VNCServerST::queryResult qr = server->queryConnection(sock, userName, | |||
&reason.buf); | |||
if (qr == VNCServerST::PENDING) | |||
return; | |||
// - If server returns ACCEPT/REJECT then pass result to SConnection | |||
approveConnection(qr == VNCServerST::ACCEPT, reason.buf); | |||
server->queryConnection(this, userName); | |||
} | |||
void VNCSConnectionST::clientInit(bool shared) | |||
{ | |||
lastEventTime = time(0); | |||
if (rfb::Server::alwaysShared || reverseConnection) shared = true; | |||
if (!(accessRights & AccessNonShared)) shared = true; | |||
if (!accessCheck(AccessNonShared)) shared = true; | |||
if (rfb::Server::neverShared) shared = false; | |||
if (!shared) { | |||
if (rfb::Server::disconnectClients && (accessRights & AccessNonShared)) { | |||
// - Close all the other connected clients | |||
vlog.debug("non-shared connection - closing clients"); | |||
server->closeClients("Non-shared connection requested", getSock()); | |||
} else { | |||
// - Refuse this connection if there are existing clients, in addition to | |||
// this one | |||
if (server->authClientCount() > 1) { | |||
close("Server is already in use"); | |||
return; | |||
} | |||
} | |||
} | |||
SConnection::clientInit(shared); | |||
server->clientReady(this, shared); | |||
} | |||
void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) | |||
@@ -504,36 +458,29 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) | |||
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask) | |||
{ | |||
pointerEventTime = lastEventTime = time(0); | |||
server->lastUserInputTime = lastEventTime; | |||
if (!(accessRights & AccessPtrEvents)) return; | |||
if (!accessCheck(AccessPtrEvents)) return; | |||
if (!rfb::Server::acceptPointerEvents) return; | |||
if (!server->pointerClient || server->pointerClient == this) { | |||
pointerEventPos = pos; | |||
if (buttonMask) | |||
server->pointerClient = this; | |||
else | |||
server->pointerClient = 0; | |||
server->desktop->pointerEvent(pointerEventPos, buttonMask); | |||
} | |||
pointerEventPos = pos; | |||
server->pointerEvent(this, pointerEventPos, buttonMask); | |||
} | |||
class VNCSConnectionSTShiftPresser { | |||
public: | |||
VNCSConnectionSTShiftPresser(SDesktop* desktop_) | |||
: desktop(desktop_), pressed(false) {} | |||
VNCSConnectionSTShiftPresser(VNCServerST* server_) | |||
: server(server_), pressed(false) {} | |||
~VNCSConnectionSTShiftPresser() { | |||
if (pressed) { | |||
vlog.debug("Releasing fake Shift_L"); | |||
desktop->keyEvent(XK_Shift_L, 0, false); | |||
server->keyEvent(XK_Shift_L, 0, false); | |||
} | |||
} | |||
void press() { | |||
vlog.debug("Pressing fake Shift_L"); | |||
desktop->keyEvent(XK_Shift_L, 0, true); | |||
server->keyEvent(XK_Shift_L, 0, true); | |||
pressed = true; | |||
} | |||
SDesktop* desktop; | |||
VNCServerST* server; | |||
bool pressed; | |||
}; | |||
@@ -543,8 +490,7 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
rdr::U32 lookup; | |||
lastEventTime = time(0); | |||
server->lastUserInputTime = lastEventTime; | |||
if (!(accessRights & AccessKeyEvents)) return; | |||
if (!accessCheck(AccessKeyEvents)) return; | |||
if (!rfb::Server::acceptKeyEvents) return; | |||
if (down) | |||
@@ -552,18 +498,8 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
else | |||
vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode); | |||
// Remap the key if required | |||
if (server->keyRemapper) { | |||
rdr::U32 newkey; | |||
newkey = server->keyRemapper->remapKey(keysym); | |||
if (newkey != keysym) { | |||
vlog.debug("Key remapped to 0x%x", newkey); | |||
keysym = newkey; | |||
} | |||
} | |||
// Avoid lock keys if we don't know the server state | |||
if ((server->ledState == ledUnknown) && | |||
if ((server->getLEDState() == ledUnknown) && | |||
((keysym == XK_Caps_Lock) || | |||
(keysym == XK_Num_Lock) || | |||
(keysym == XK_Scroll_Lock))) { | |||
@@ -581,7 +517,7 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
return; | |||
} | |||
if (down && (server->ledState != ledUnknown)) { | |||
if (down && (server->getLEDState() != ledUnknown)) { | |||
// CapsLock synchronisation heuristic | |||
// (this assumes standard interaction between CapsLock the Shift | |||
// keys and normal characters) | |||
@@ -591,12 +527,12 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
uppercase = (keysym >= XK_A) && (keysym <= XK_Z); | |||
shift = isShiftPressed(); | |||
lock = server->ledState & ledCapsLock; | |||
lock = server->getLEDState() & ledCapsLock; | |||
if (lock == (uppercase == shift)) { | |||
vlog.debug("Inserting fake CapsLock to get in sync with client"); | |||
server->desktop->keyEvent(XK_Caps_Lock, 0, true); | |||
server->desktop->keyEvent(XK_Caps_Lock, 0, false); | |||
server->keyEvent(XK_Caps_Lock, 0, true); | |||
server->keyEvent(XK_Caps_Lock, 0, false); | |||
} | |||
} | |||
@@ -611,7 +547,7 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) || | |||
(keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal); | |||
shift = isShiftPressed(); | |||
lock = server->ledState & ledNumLock; | |||
lock = server->getLEDState() & ledNumLock; | |||
if (shift) { | |||
// We don't know the appropriate NumLock state for when Shift | |||
@@ -625,15 +561,15 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
// | |||
} else if (lock == (number == shift)) { | |||
vlog.debug("Inserting fake NumLock to get in sync with client"); | |||
server->desktop->keyEvent(XK_Num_Lock, 0, true); | |||
server->desktop->keyEvent(XK_Num_Lock, 0, false); | |||
server->keyEvent(XK_Num_Lock, 0, true); | |||
server->keyEvent(XK_Num_Lock, 0, false); | |||
} | |||
} | |||
} | |||
} | |||
// Turn ISO_Left_Tab into shifted Tab. | |||
VNCSConnectionSTShiftPresser shiftPresser(server->desktop); | |||
VNCSConnectionSTShiftPresser shiftPresser(server); | |||
if (keysym == XK_ISO_Left_Tab) { | |||
if (!isShiftPressed()) | |||
shiftPresser.press(); | |||
@@ -659,21 +595,21 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
return; | |||
} | |||
server->desktop->keyEvent(keysym, keycode, down); | |||
server->keyEvent(keysym, keycode, down); | |||
} | |||
void VNCSConnectionST::clientCutText(const char* str, int len) | |||
{ | |||
if (!(accessRights & AccessCutText)) return; | |||
if (!accessCheck(AccessCutText)) return; | |||
if (!rfb::Server::acceptCutText) return; | |||
server->desktop->clientCutText(str, len); | |||
server->clientCutText(str, len); | |||
} | |||
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) | |||
{ | |||
Rect safeRect; | |||
if (!(accessRights & AccessView)) return; | |||
if (!accessCheck(AccessView)) return; | |||
SConnection::framebufferUpdateRequest(r, incremental); | |||
@@ -712,30 +648,12 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height, | |||
{ | |||
unsigned int result; | |||
if (!(accessRights & AccessSetDesktopSize)) return; | |||
if (!accessCheck(AccessSetDesktopSize)) return; | |||
if (!rfb::Server::acceptSetDesktopSize) return; | |||
// Don't bother the desktop with an invalid configuration | |||
if (!layout.validate(fb_width, fb_height)) { | |||
writer()->writeExtendedDesktopSize(reasonClient, resultInvalid, | |||
fb_width, fb_height, layout); | |||
return; | |||
} | |||
// FIXME: the desktop will call back to VNCServerST and an extra set | |||
// of ExtendedDesktopSize messages will be sent. This is okay | |||
// protocol-wise, but unnecessary. | |||
result = server->desktop->setScreenLayout(fb_width, fb_height, layout); | |||
result = server->setDesktopSize(this, fb_width, fb_height, layout); | |||
writer()->writeExtendedDesktopSize(reasonClient, result, | |||
fb_width, fb_height, layout); | |||
// Only notify other clients on success | |||
if (result == resultSuccess) { | |||
if (server->screenLayout != layout) | |||
throw Exception("Desktop configured a different screen layout than requested"); | |||
server->notifyScreenLayoutChange(this); | |||
} | |||
} | |||
void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[]) | |||
@@ -1001,7 +919,7 @@ void VNCSConnectionST::writeDataUpdate() | |||
bogusCopiedCursor = damagedCursorRegion; | |||
bogusCopiedCursor.translate(ui.copy_delta); | |||
bogusCopiedCursor.assign_intersect(server->pb->getRect()); | |||
bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect()); | |||
if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) { | |||
updates.add_changed(bogusCopiedCursor); | |||
needNewUpdateInfo = true; | |||
@@ -1124,7 +1042,7 @@ void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) | |||
if (!authenticated()) | |||
return; | |||
cp.screenLayout = server->screenLayout; | |||
cp.screenLayout = server->getScreenLayout(); | |||
if (state() != RFBSTATE_NORMAL) | |||
return; | |||
@@ -1148,7 +1066,7 @@ void VNCSConnectionST::setCursor() | |||
cp.setCursor(emptyCursor); | |||
clientHasCursor = false; | |||
} else { | |||
cp.setCursor(*server->cursor); | |||
cp.setCursor(*server->getCursor()); | |||
clientHasCursor = true; | |||
} | |||
@@ -1194,37 +1112,3 @@ void VNCSConnectionST::setSocketTimeouts() | |||
sock->inStream().setTimeout(timeoutms); | |||
sock->outStream().setTimeout(timeoutms); | |||
} | |||
char* VNCSConnectionST::getStartTime() | |||
{ | |||
char* result = ctime(&startTime); | |||
result[24] = '\0'; | |||
return result; | |||
} | |||
void VNCSConnectionST::setStatus(int status) | |||
{ | |||
switch (status) { | |||
case 0: | |||
accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView; | |||
break; | |||
case 1: | |||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents)) | AccessView; | |||
break; | |||
case 2: | |||
accessRights = accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessView); | |||
break; | |||
} | |||
framebufferUpdateRequest(server->pb->getRect(), false); | |||
} | |||
int VNCSConnectionST::getStatus() | |||
{ | |||
if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007) | |||
return 0; | |||
if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001) | |||
return 1; | |||
if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000) | |||
return 2; | |||
return 4; | |||
} | |||
@@ -43,21 +43,18 @@ namespace rfb { | |||
VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse); | |||
virtual ~VNCSConnectionST(); | |||
// SConnection methods | |||
virtual bool accessCheck(AccessRights ar) const; | |||
virtual void close(const char* reason); | |||
// Methods called from VNCServerST. None of these methods ever knowingly | |||
// throw an exception. | |||
// Unless otherwise stated, the SConnectionST may not be valid after any of | |||
// these methods are called, since they catch exceptions and may have | |||
// called close() which deletes the object. | |||
// init() must be called to initialise the protocol. If it fails it | |||
// returns false, and close() will have been called. | |||
bool init(); | |||
// close() shuts down the socket to the client and deletes the | |||
// SConnectionST object. | |||
void close(const char* reason); | |||
// processMessages() processes incoming messages from the client, invoking | |||
// various callbacks as a result. It continues to process messages until | |||
// reading might block. shutdown() will be called on the connection's | |||
@@ -78,14 +75,14 @@ namespace rfb { | |||
void serverCutTextOrClose(const char *str, int len); | |||
void setDesktopNameOrClose(const char *name); | |||
void setLEDStateOrClose(unsigned int state); | |||
void approveConnectionOrClose(bool accept, const char* reason); | |||
// checkIdleTimeout() returns the number of milliseconds left until the | |||
// idle timeout expires. If it has expired, the connection is closed and | |||
// zero is returned. Zero is also returned if there is no idle timeout. | |||
int checkIdleTimeout(); | |||
// The following methods never throw exceptions nor do they ever delete the | |||
// SConnectionST object. | |||
// The following methods never throw exceptions | |||
// getComparerState() returns if this client would like the framebuffer | |||
// comparer to be enabled. | |||
@@ -103,32 +100,18 @@ namespace rfb { | |||
bool needRenderedCursor(); | |||
network::Socket* getSock() { return sock; } | |||
// Change tracking | |||
void add_changed(const Region& region) { updates.add_changed(region); } | |||
void add_copied(const Region& dest, const Point& delta) { | |||
updates.add_copied(dest, delta); | |||
} | |||
const char* getPeerEndpoint() const {return peerEndpoint.buf;} | |||
// approveConnectionOrClose() is called some time after | |||
// VNCServerST::queryConnection() has returned with PENDING to accept or | |||
// reject the connection. The accept argument should be true for | |||
// acceptance, or false for rejection, in which case a string reason may | |||
// also be given. | |||
void approveConnectionOrClose(bool accept, const char* reason); | |||
char* getStartTime(); | |||
void setStatus(int status); | |||
int getStatus(); | |||
private: | |||
// SConnection callbacks | |||
// These methods are invoked as callbacks from processMsg(). Note that | |||
// none of these methods should call any of the above methods which may | |||
// delete the SConnectionST object. | |||
// These methods are invoked as callbacks from processMsg() | |||
virtual void authSuccess(); | |||
virtual void queryConnection(const char* userName); | |||
@@ -148,12 +131,6 @@ namespace rfb { | |||
virtual void supportsContinuousUpdates(); | |||
virtual void supportsLEDState(); | |||
// setAccessRights() allows a security package to limit the access rights | |||
// of a VNCSConnectioST to the server. These access rights are applied | |||
// such that the actual rights granted are the minimum of the server's | |||
// default access settings and the connection's access settings. | |||
virtual void setAccessRights(AccessRights ar) {accessRights=ar;} | |||
// Timer callbacks | |||
virtual bool handleTimeout(Timer* t); | |||
@@ -178,6 +155,7 @@ namespace rfb { | |||
void setLEDState(unsigned int state); | |||
void setSocketTimeouts(); | |||
private: | |||
network::Socket* sock; | |||
CharArray peerEndpoint; | |||
bool reverseConnection; | |||
@@ -209,10 +187,7 @@ namespace rfb { | |||
Point pointerEventPos; | |||
bool clientHasCursor; | |||
AccessRights accessRights; | |||
CharArray closeReason; | |||
time_t startTime; | |||
}; | |||
} | |||
#endif |
@@ -22,13 +22,16 @@ | |||
#ifndef __RFB_VNCSERVER_H__ | |||
#define __RFB_VNCSERVER_H__ | |||
#include <network/Socket.h> | |||
#include <rfb/UpdateTracker.h> | |||
#include <rfb/SSecurity.h> | |||
#include <rfb/ScreenSet.h> | |||
namespace rfb { | |||
class VNCServer : public UpdateTracker { | |||
class VNCServer : public UpdateTracker, | |||
public network::SocketServer { | |||
public: | |||
// blockUpdates()/unblockUpdates() tells the server that the pixel buffer | |||
// is currently in flux and may not be accessed. The attributes of the | |||
@@ -50,7 +53,7 @@ namespace rfb { | |||
virtual void setScreenLayout(const ScreenSet& layout) = 0; | |||
// getPixelBuffer() returns a pointer to the PixelBuffer object. | |||
virtual PixelBuffer* getPixelBuffer() const = 0; | |||
virtual const PixelBuffer* getPixelBuffer() const = 0; | |||
// serverCutText() tells the server that the cut text has changed. This | |||
// will normally be sent to all clients. | |||
@@ -59,10 +62,22 @@ namespace rfb { | |||
// bell() tells the server that it should make all clients make a bell sound. | |||
virtual void bell() = 0; | |||
// approveConnection() is called some time after | |||
// SDesktop::queryConnection() has been called, to accept or reject | |||
// the connection. The accept argument should be true for | |||
// acceptance, or false for rejection, in which case a string | |||
// reason may also be given. | |||
virtual void approveConnection(network::Socket* sock, bool accept, | |||
const char* reason = NULL) = 0; | |||
// - Close all currently-connected clients, by calling | |||
// their close() method with the supplied reason. | |||
virtual void closeClients(const char* reason) = 0; | |||
// getConnection() gets the SConnection for a particular Socket. If | |||
// the Socket is not recognised then null is returned. | |||
virtual SConnection* getConnection(network::Socket* sock) = 0; | |||
// setCursor() tells the server that the cursor has changed. The | |||
// cursorData argument contains width*height rgba quadruplets with | |||
// non-premultiplied alpha. |
@@ -53,7 +53,7 @@ | |||
#include <rfb/ComparingUpdateTracker.h> | |||
#include <rfb/KeyRemapper.h> | |||
#include <rfb/ListConnInfo.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/Security.h> | |||
#include <rfb/ServerCore.h> | |||
#include <rfb/VNCServerST.h> | |||
@@ -66,7 +66,7 @@ | |||
using namespace rfb; | |||
static LogWriter slog("VNCServerST"); | |||
LogWriter VNCServerST::connectionsLog("Connections"); | |||
static LogWriter connectionsLog("Connections"); | |||
// | |||
// -=- VNCServerST Implementation | |||
@@ -80,9 +80,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) | |||
name(strDup(name_)), pointerClient(0), comparer(0), | |||
cursor(new Cursor(0, 0, Point(), NULL)), | |||
renderedCursorInvalid(false), | |||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance), | |||
lastConnectionTime(0), disableclients(false), | |||
frameTimer(this) | |||
keyRemapper(&KeyRemapper::defInstance), | |||
lastConnectionTime(0), frameTimer(this) | |||
{ | |||
lastUserInputTime = lastDisconnectTime = time(0); | |||
slog.debug("creating single-threaded server %s", name.buf); | |||
@@ -99,9 +98,11 @@ VNCServerST::~VNCServerST() | |||
stopFrameClock(); | |||
// Delete all the clients, and their sockets, and any closing sockets | |||
// NB: Deleting a client implicitly removes it from the clients list | |||
while (!clients.empty()) { | |||
delete clients.front(); | |||
VNCSConnectionST* client; | |||
client = clients.front(); | |||
clients.pop_front(); | |||
delete client; | |||
} | |||
// Stop the desktop object if active, *only* after deleting all clients! | |||
@@ -125,8 +126,13 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) | |||
if (blHosts->isBlackmarked(address.buf)) { | |||
connectionsLog.error("blacklisted: %s", address.buf); | |||
try { | |||
SConnection::writeConnFailedFromScratch("Too many security failures", | |||
&sock->outStream()); | |||
rdr::OutStream& os = sock->outStream(); | |||
// Shortest possible way to tell a client it is not welcome | |||
os.writeBytes("RFB 003.003\n", 12); | |||
os.writeU32(0); | |||
os.writeString("Too many security failures"); | |||
os.flush(); | |||
} catch (rdr::Exception&) { | |||
} | |||
sock->shutdown(); | |||
@@ -134,11 +140,16 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) | |||
return; | |||
} | |||
CharArray name; | |||
name.buf = sock->getPeerEndpoint(); | |||
connectionsLog.status("accepted: %s", name.buf); | |||
if (clients.empty()) { | |||
lastConnectionTime = time(0); | |||
} | |||
VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); | |||
clients.push_front(client); | |||
client->init(); | |||
} | |||
@@ -147,9 +158,22 @@ void VNCServerST::removeSocket(network::Socket* sock) { | |||
std::list<VNCSConnectionST*>::iterator ci; | |||
for (ci = clients.begin(); ci != clients.end(); ci++) { | |||
if ((*ci)->getSock() == sock) { | |||
clients.remove(*ci); | |||
// - Release the cursor if this client owns it | |||
if (pointerClient == *ci) | |||
pointerClient = NULL; | |||
if ((*ci)->authenticated()) | |||
lastDisconnectTime = time(0); | |||
// - Delete the per-Socket resources | |||
delete *ci; | |||
CharArray name; | |||
name.buf = sock->getPeerEndpoint(); | |||
connectionsLog.status("closed: %s", name.buf); | |||
// - Check that the desktop object is still required | |||
if (authClientCount() == 0) | |||
stopDesktop(); | |||
@@ -467,6 +491,81 @@ void VNCServerST::setLEDState(unsigned int state) | |||
} | |||
} | |||
// Event handlers | |||
void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
{ | |||
lastUserInputTime = time(0); | |||
// Remap the key if required | |||
if (keyRemapper) { | |||
rdr::U32 newkey; | |||
newkey = keyRemapper->remapKey(keysym); | |||
if (newkey != keysym) { | |||
slog.debug("Key remapped to 0x%x", newkey); | |||
keysym = newkey; | |||
} | |||
} | |||
desktop->keyEvent(keysym, keycode, down); | |||
} | |||
void VNCServerST::pointerEvent(VNCSConnectionST* client, | |||
const Point& pos, int buttonMask) | |||
{ | |||
lastUserInputTime = time(0); | |||
// Let one client own the cursor whilst buttons are pressed in order | |||
// to provide a bit more sane user experience | |||
if ((pointerClient != NULL) && (pointerClient != client)) | |||
return; | |||
if (buttonMask) | |||
pointerClient = client; | |||
else | |||
pointerClient = NULL; | |||
desktop->pointerEvent(pos, buttonMask); | |||
} | |||
void VNCServerST::clientCutText(const char* str, int len) | |||
{ | |||
desktop->clientCutText(str, len); | |||
} | |||
unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester, | |||
int fb_width, int fb_height, | |||
const ScreenSet& layout) | |||
{ | |||
unsigned int result; | |||
std::list<VNCSConnectionST*>::iterator ci, ci_next; | |||
// Don't bother the desktop with an invalid configuration | |||
if (!layout.validate(fb_width, fb_height)) | |||
return resultInvalid; | |||
// FIXME: the desktop will call back to VNCServerST and an extra set | |||
// of ExtendedDesktopSize messages will be sent. This is okay | |||
// protocol-wise, but unnecessary. | |||
result = desktop->setScreenLayout(fb_width, fb_height, layout); | |||
if (result != resultSuccess) | |||
return result; | |||
// Sanity check | |||
if (screenLayout != layout) | |||
throw Exception("Desktop configured a different screen layout than requested"); | |||
// Notify other clients | |||
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { | |||
ci_next = ci; ci_next++; | |||
if ((*ci) == requester) | |||
continue; | |||
(*ci)->screenLayoutChangeOrClose(reasonOtherClient); | |||
} | |||
return resultSuccess; | |||
} | |||
// Other public methods | |||
void VNCServerST::approveConnection(network::Socket* sock, bool accept, | |||
@@ -504,7 +603,7 @@ void VNCServerST::getSockets(std::list<network::Socket*>* sockets) | |||
} | |||
} | |||
SConnection* VNCServerST::getSConnection(network::Socket* sock) { | |||
SConnection* VNCServerST::getConnection(network::Socket* sock) { | |||
std::list<VNCSConnectionST*>::iterator ci; | |||
for (ci = clients.begin(); ci != clients.end(); ci++) { | |||
if ((*ci)->getSock() == sock) | |||
@@ -534,6 +633,63 @@ bool VNCServerST::handleTimeout(Timer* t) | |||
return false; | |||
} | |||
void VNCServerST::queryConnection(VNCSConnectionST* client, | |||
const char* userName) | |||
{ | |||
// - Authentication succeeded - clear from blacklist | |||
CharArray name; | |||
name.buf = client->getSock()->getPeerAddress(); | |||
blHosts->clearBlackmark(name.buf); | |||
// - Prepare the desktop for that the client will start requiring | |||
// resources after this | |||
startDesktop(); | |||
// - Special case to provide a more useful error message | |||
if (rfb::Server::neverShared && | |||
!rfb::Server::disconnectClients && | |||
authClientCount() > 0) { | |||
approveConnection(client->getSock(), false, | |||
"The server is already in use"); | |||
return; | |||
} | |||
// - Are we configured to do queries? | |||
if (!rfb::Server::queryConnect && | |||
!client->getSock()->requiresQuery()) { | |||
approveConnection(client->getSock(), true, NULL); | |||
return; | |||
} | |||
// - Does the client have the right to bypass the query? | |||
if (client->accessCheck(SConnection::AccessNoQuery)) | |||
{ | |||
approveConnection(client->getSock(), true, NULL); | |||
return; | |||
} | |||
desktop->queryConnection(client->getSock(), userName); | |||
} | |||
void VNCServerST::clientReady(VNCSConnectionST* client, bool shared) | |||
{ | |||
if (!shared) { | |||
if (rfb::Server::disconnectClients && | |||
client->accessCheck(SConnection::AccessNonShared)) { | |||
// - Close all the other connected clients | |||
slog.debug("non-shared connection - closing clients"); | |||
closeClients("Non-shared connection requested", client->getSock()); | |||
} else { | |||
// - Refuse this connection if there are existing clients, in addition to | |||
// this one | |||
if (authClientCount() > 1) { | |||
client->close("Server is already in use"); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
// -=- Internal methods | |||
void VNCServerST::startDesktop() | |||
@@ -689,50 +845,6 @@ const RenderedCursor* VNCServerST::getRenderedCursor() | |||
return &renderedCursor; | |||
} | |||
void VNCServerST::getConnInfo(ListConnInfo * listConn) | |||
{ | |||
listConn->Clear(); | |||
listConn->setDisable(getDisable()); | |||
if (clients.empty()) | |||
return; | |||
std::list<VNCSConnectionST*>::iterator i; | |||
for (i = clients.begin(); i != clients.end(); i++) | |||
listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(), | |||
(*i)->getStartTime(), (*i)->getStatus()); | |||
} | |||
void VNCServerST::setConnStatus(ListConnInfo* listConn) | |||
{ | |||
setDisable(listConn->getDisable()); | |||
if (listConn->Empty() || clients.empty()) return; | |||
for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) { | |||
VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn(); | |||
std::list<VNCSConnectionST*>::iterator i; | |||
for (i = clients.begin(); i != clients.end(); i++) { | |||
if ((*i) == conn) { | |||
int status = listConn->iGetStatus(); | |||
if (status == 3) { | |||
(*i)->close(0); | |||
} else { | |||
(*i)->setStatus(status); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester) | |||
{ | |||
std::list<VNCSConnectionST*>::iterator ci, ci_next; | |||
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { | |||
ci_next = ci; ci_next++; | |||
if ((*ci) == requester) | |||
continue; | |||
(*ci)->screenLayoutChangeOrClose(reasonOtherClient); | |||
} | |||
} | |||
bool VNCServerST::getComparerState() | |||
{ | |||
if (rfb::Server::compareFB == 0) |
@@ -28,11 +28,9 @@ | |||
#include <rfb/SDesktop.h> | |||
#include <rfb/VNCServer.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/Blacklist.h> | |||
#include <rfb/Cursor.h> | |||
#include <rfb/Timer.h> | |||
#include <network/Socket.h> | |||
#include <rfb/ScreenSet.h> | |||
namespace rfb { | |||
@@ -44,8 +42,7 @@ namespace rfb { | |||
class KeyRemapper; | |||
class VNCServerST : public VNCServer, | |||
public Timer::Callback, | |||
public network::SocketServer { | |||
public Timer::Callback { | |||
public: | |||
// -=- Constructors | |||
@@ -93,102 +90,70 @@ namespace rfb { | |||
virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout); | |||
virtual void setPixelBuffer(PixelBuffer* pb); | |||
virtual void setScreenLayout(const ScreenSet& layout); | |||
virtual PixelBuffer* getPixelBuffer() const { return pb; } | |||
virtual const PixelBuffer* getPixelBuffer() const { return pb; } | |||
virtual void serverCutText(const char* str, int len); | |||
virtual void approveConnection(network::Socket* sock, bool accept, | |||
const char* reason); | |||
virtual void closeClients(const char* reason) {closeClients(reason, 0);} | |||
virtual SConnection* getConnection(network::Socket* sock); | |||
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, | |||
const rdr::U8* data); | |||
virtual void setCursorPos(const Point& p); | |||
virtual void setName(const char* name_); | |||
virtual void setLEDState(unsigned state); | |||
virtual void bell(); | |||
// - Close all currently-connected clients, by calling | |||
// their close() method with the supplied reason. | |||
virtual void closeClients(const char* reason) {closeClients(reason, 0);} | |||
// VNCServerST-only methods | |||
// Methods to get the currently set server state | |||
const ScreenSet& getScreenLayout() const { return screenLayout; } | |||
const Cursor* getCursor() const { return cursor; } | |||
const Point& getCursorPos() const { return cursorPos; } | |||
const char* getName() const { return name.buf; } | |||
unsigned getLEDState() const { return ledState; } | |||
// Event handlers | |||
void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
void pointerEvent(VNCSConnectionST* client, const Point& pos, int buttonMask); | |||
void clientCutText(const char* str, int len); | |||
unsigned int setDesktopSize(VNCSConnectionST* requester, | |||
int fb_width, int fb_height, | |||
const ScreenSet& layout); | |||
// closeClients() closes all RFB sessions, except the specified one (if | |||
// any), and logs the specified reason for closure. | |||
void closeClients(const char* reason, network::Socket* sock); | |||
// getSConnection() gets the SConnection for a particular Socket. If | |||
// the Socket is not recognised then null is returned. | |||
// queryConnection() does some basic checks and then passes on the | |||
// request to the desktop. | |||
void queryConnection(VNCSConnectionST* client, const char* userName); | |||
SConnection* getSConnection(network::Socket* sock); | |||
// clientReady() is called by a VNCSConnectionST instance when the | |||
// client has completed the handshake and is ready for normal | |||
// communication. | |||
void clientReady(VNCSConnectionST* client, bool shared); | |||
// getName() returns the name of this VNC Server. NB: The value returned | |||
// is the server's internal buffer which may change after any other methods | |||
// are called - take a copy if necessary. | |||
const char* getName() const {return name.buf;} | |||
// Estimated time until the next time new updates will be pushed | |||
// to clients | |||
int msToNextUpdate(); | |||
// setName() specifies the desktop name that the server should provide to | |||
// clients | |||
virtual void setName(const char* name_); | |||
// Part of the framebuffer that has been modified but is not yet | |||
// ready to be sent to clients | |||
Region getPendingRegion(); | |||
// A QueryConnectionHandler, if supplied, is passed details of incoming | |||
// connections to approve, reject, or query the user about. | |||
// | |||
// queryConnection() is called when a connection has been | |||
// successfully authenticated. The sock and userName arguments identify | |||
// the socket and the name of the authenticated user, if any. It should | |||
// return ACCEPT if the connection should be accepted, REJECT if it should | |||
// be rejected, or PENDING if a decision cannot yet be reached. If REJECT | |||
// is returned, *reason can be set to a string describing the reason - this | |||
// will be delete[]ed when it is finished with. If PENDING is returned, | |||
// approveConnection() must be called some time later to accept or reject | |||
// the connection. | |||
enum queryResult { ACCEPT, REJECT, PENDING }; | |||
struct QueryConnectionHandler { | |||
virtual ~QueryConnectionHandler() {} | |||
virtual queryResult queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) = 0; | |||
}; | |||
void setQueryConnectionHandler(QueryConnectionHandler* qch) { | |||
queryConnectionHandler = qch; | |||
} | |||
// queryConnection is called as described above, and either passes the | |||
// request on to the registered handler, or accepts the connection if | |||
// no handler has been specified. | |||
virtual queryResult queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) { | |||
return queryConnectionHandler | |||
? queryConnectionHandler->queryConnection(sock, userName, reason) | |||
: ACCEPT; | |||
} | |||
// approveConnection() is called by the active QueryConnectionHandler, | |||
// some time after queryConnection() has returned with PENDING, to accept | |||
// or reject the connection. The accept argument should be true for | |||
// acceptance, or false for rejection, in which case a string reason may | |||
// also be given. | |||
void approveConnection(network::Socket* sock, bool accept, | |||
const char* reason); | |||
// setBlacklist() is called to replace the VNCServerST's internal | |||
// Blacklist instance with another instance. This allows a single | |||
// Blacklist to be shared by multiple VNCServerST instances. | |||
void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;} | |||
// setKeyRemapper() replaces the VNCServerST's default key remapper. | |||
// NB: A null pointer is valid here. | |||
void setKeyRemapper(KeyRemapper* kr) { keyRemapper = kr; } | |||
void getConnInfo(ListConnInfo * listConn); | |||
void setConnStatus(ListConnInfo* listConn); | |||
bool getDisable() { return disableclients;}; | |||
void setDisable(bool disable) { disableclients = disable;}; | |||
// getRenderedCursor() returns an up to date version of the server | |||
// side rendered cursor buffer | |||
const RenderedCursor* getRenderedCursor(); | |||
protected: | |||
friend class VNCSConnectionST; | |||
// Timer callbacks | |||
virtual bool handleTimeout(Timer* t); | |||
@@ -197,7 +162,17 @@ namespace rfb { | |||
void startDesktop(); | |||
void stopDesktop(); | |||
static LogWriter connectionsLog; | |||
// - Check how many of the clients are authenticated. | |||
int authClientCount(); | |||
bool needRenderedCursor(); | |||
void startFrameClock(); | |||
void stopFrameClock(); | |||
void writeUpdate(); | |||
bool getComparerState(); | |||
protected: | |||
Blacklist blacklist; | |||
Blacklist* blHosts; | |||
@@ -221,30 +196,12 @@ namespace rfb { | |||
RenderedCursor renderedCursor; | |||
bool renderedCursorInvalid; | |||
// - Check how many of the clients are authenticated. | |||
int authClientCount(); | |||
bool needRenderedCursor(); | |||
void startFrameClock(); | |||
void stopFrameClock(); | |||
int msToNextUpdate(); | |||
void writeUpdate(); | |||
Region getPendingRegion(); | |||
const RenderedCursor* getRenderedCursor(); | |||
void notifyScreenLayoutChange(VNCSConnectionST *requester); | |||
bool getComparerState(); | |||
QueryConnectionHandler* queryConnectionHandler; | |||
KeyRemapper* keyRemapper; | |||
time_t lastUserInputTime; | |||
time_t lastDisconnectTime; | |||
time_t lastConnectionTime; | |||
bool disableclients; | |||
Timer frameTimer; | |||
}; | |||
@@ -18,6 +18,10 @@ | |||
* USA. | |||
*/ | |||
#include <assert.h> | |||
#include <rfb/LogWriter.h> | |||
#include <x0vncserver/XDesktop.h> | |||
#include <X11/XKBlib.h> | |||
@@ -53,6 +57,10 @@ BoolParameter rawKeyboard("RawKeyboard", | |||
"Send keyboard events straight through and " | |||
"avoid mapping them to the current keyboard " | |||
"layout", false); | |||
IntParameter queryConnectTimeout("QueryConnectTimeout", | |||
"Number of seconds to show the Accept Connection dialog before " | |||
"rejecting the connection", | |||
10); | |||
static rfb::LogWriter vlog("XDesktop"); | |||
@@ -63,6 +71,7 @@ static const char * ledNames[XDESKTOP_N_LEDS] = { | |||
XDesktop::XDesktop(Display* dpy_, Geometry *geometry_) | |||
: dpy(dpy_), geometry(geometry_), pb(0), server(0), | |||
queryConnectDialog(0), queryConnectSock(0), | |||
oldButtonMask(0), haveXtest(false), haveDamage(false), | |||
maxButtons(0), running(false), ledMasks(), ledState(0), | |||
codeMap(0), codeMapLen(0) | |||
@@ -227,7 +236,7 @@ void XDesktop::start(VNCServer* vs) { | |||
pb = new XPixelBuffer(dpy, factory, geometry->getRect()); | |||
vlog.info("Allocated %s", pb->getImage()->classDesc()); | |||
server = (VNCServerST *)vs; | |||
server = vs; | |||
server->setPixelBuffer(pb, computeScreenLayout()); | |||
#ifdef HAVE_XDAMAGE | |||
@@ -254,6 +263,9 @@ void XDesktop::stop() { | |||
XDamageDestroy(dpy, damage); | |||
#endif | |||
delete queryConnectDialog; | |||
queryConnectDialog = 0; | |||
server->setPixelBuffer(0); | |||
server = 0; | |||
@@ -265,6 +277,30 @@ bool XDesktop::isRunning() { | |||
return running; | |||
} | |||
void XDesktop::queryConnection(network::Socket* sock, | |||
const char* userName) | |||
{ | |||
assert(isRunning()); | |||
if (queryConnectSock) { | |||
server->approveConnection(sock, false, "Another connection is currently being queried."); | |||
return; | |||
} | |||
if (!userName) | |||
userName = "(anonymous)"; | |||
queryConnectSock = sock; | |||
CharArray address(sock->getPeerAddress()); | |||
delete queryConnectDialog; | |||
queryConnectDialog = new QueryConnectDialog(dpy, address.buf, | |||
userName, | |||
queryConnectTimeout, | |||
this); | |||
queryConnectDialog->map(); | |||
} | |||
void XDesktop::pointerEvent(const Point& pos, int buttonMask) { | |||
#ifdef HAVE_XTEST | |||
if (!haveXtest) return; | |||
@@ -686,6 +722,21 @@ bool XDesktop::handleGlobalEvent(XEvent* ev) { | |||
return false; | |||
} | |||
void XDesktop::queryApproved() | |||
{ | |||
assert(isRunning()); | |||
server->approveConnection(queryConnectSock, true, 0); | |||
queryConnectSock = 0; | |||
} | |||
void XDesktop::queryRejected() | |||
{ | |||
assert(isRunning()); | |||
server->approveConnection(queryConnectSock, false, | |||
"Connection rejected by local user"); | |||
queryConnectSock = 0; | |||
} | |||
bool XDesktop::setCursor() | |||
{ | |||
XFixesCursorImage *cim; |
@@ -21,7 +21,7 @@ | |||
#ifndef __XDESKTOP_H__ | |||
#define __XDESKTOP_H__ | |||
#include <rfb/VNCServerST.h> | |||
#include <rfb/SDesktop.h> | |||
#include <tx/TXWindow.h> | |||
#include <unixcommon.h> | |||
@@ -30,13 +30,17 @@ | |||
#include <X11/extensions/Xdamage.h> | |||
#endif | |||
#include <vncconfig/QueryConnectDialog.h> | |||
class Geometry; | |||
class XPixelBuffer; | |||
// number of XKb indicator leds to handle | |||
#define XDESKTOP_N_LEDS 3 | |||
class XDesktop : public rfb::SDesktop, public TXGlobalEventHandler | |||
class XDesktop : public rfb::SDesktop, | |||
public TXGlobalEventHandler, | |||
public QueryResultCallback | |||
{ | |||
public: | |||
XDesktop(Display* dpy_, Geometry *geometry); | |||
@@ -46,6 +50,8 @@ public: | |||
virtual void start(rfb::VNCServer* vs); | |||
virtual void stop(); | |||
bool isRunning(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); | |||
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down); | |||
@@ -56,11 +62,17 @@ public: | |||
// -=- TXGlobalEventHandler interface | |||
virtual bool handleGlobalEvent(XEvent* ev); | |||
// -=- QueryResultCallback interface | |||
virtual void queryApproved(); | |||
virtual void queryRejected(); | |||
protected: | |||
Display* dpy; | |||
Geometry* geometry; | |||
XPixelBuffer* pb; | |||
rfb::VNCServerST* server; | |||
rfb::VNCServer* server; | |||
QueryConnectDialog* queryConnectDialog; | |||
network::Socket* queryConnectSock; | |||
int oldButtonMask; | |||
bool haveXtest; | |||
bool haveDamage; |
@@ -33,8 +33,6 @@ | |||
#include <network/TcpSocket.h> | |||
#include <network/UnixSocket.h> | |||
#include <vncconfig/QueryConnectDialog.h> | |||
#include <signal.h> | |||
#include <X11/X.h> | |||
#include <X11/Xlib.h> | |||
@@ -61,10 +59,6 @@ StringParameter displayname("display", "The X display", ""); | |||
IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900); | |||
StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", ""); | |||
IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600); | |||
IntParameter queryConnectTimeout("QueryConnectTimeout", | |||
"Number of seconds to show the Accept Connection dialog before " | |||
"rejecting the connection", | |||
10); | |||
StringParameter hostsFile("HostsFile", "File with IP access control rules", ""); | |||
// | |||
@@ -79,50 +73,6 @@ static void CleanupSignalHandler(int sig) | |||
} | |||
class QueryConnHandler : public VNCServerST::QueryConnectionHandler, | |||
public QueryResultCallback { | |||
public: | |||
QueryConnHandler(Display* dpy, VNCServerST* vs) | |||
: display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {} | |||
~QueryConnHandler() { delete queryConnectDialog; } | |||
// -=- VNCServerST::QueryConnectionHandler interface | |||
virtual VNCServerST::queryResult queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) { | |||
if (queryConnectSock) { | |||
*reason = strDup("Another connection is currently being queried."); | |||
return VNCServerST::REJECT; | |||
} | |||
if (!userName) userName = "(anonymous)"; | |||
queryConnectSock = sock; | |||
CharArray address(sock->getPeerAddress()); | |||
delete queryConnectDialog; | |||
queryConnectDialog = new QueryConnectDialog(display, address.buf, | |||
userName, queryConnectTimeout, | |||
this); | |||
queryConnectDialog->map(); | |||
return VNCServerST::PENDING; | |||
} | |||
// -=- QueryResultCallback interface | |||
virtual void queryApproved() { | |||
server->approveConnection(queryConnectSock, true, 0); | |||
queryConnectSock = 0; | |||
} | |||
virtual void queryRejected() { | |||
server->approveConnection(queryConnectSock, false, | |||
"Connection rejected by local user"); | |||
queryConnectSock = 0; | |||
} | |||
private: | |||
Display* display; | |||
VNCServerST* server; | |||
QueryConnectDialog* queryConnectDialog; | |||
network::Socket* queryConnectSock; | |||
}; | |||
class FileTcpFilter : public TcpFilter | |||
{ | |||
@@ -307,8 +257,6 @@ int main(int argc, char** argv) | |||
XDesktop desktop(dpy, &geo); | |||
VNCServerST server("x0vncserver", &desktop); | |||
QueryConnHandler qcHandler(dpy, &server); | |||
server.setQueryConnectionHandler(&qcHandler); | |||
if (rfbunixpath.getValueStr()[0] != '\0') { | |||
listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode)); |
@@ -81,7 +81,6 @@ XserverDesktop::XserverDesktop(int screenIndex_, | |||
server = new VNCServerST(name, this); | |||
setFramebuffer(width, height, fbptr, stride); | |||
server->setQueryConnectionHandler(this); | |||
for (std::list<SocketListener*>::iterator i = listeners.begin(); | |||
i != listeners.end(); | |||
@@ -145,22 +144,31 @@ void XserverDesktop::refreshScreenLayout() | |||
server->setScreenLayout(::computeScreenLayout(&outputIdMap)); | |||
} | |||
rfb::VNCServerST::queryResult | |||
XserverDesktop::queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) | |||
void XserverDesktop::start(rfb::VNCServer* vs) | |||
{ | |||
// We already own the server object, and we always keep it in a | |||
// ready state | |||
assert(vs == server); | |||
} | |||
void XserverDesktop::stop() | |||
{ | |||
} | |||
void XserverDesktop::queryConnection(network::Socket* sock, | |||
const char* userName) | |||
{ | |||
int count; | |||
if (queryConnectTimer.isStarted()) { | |||
*reason = strDup("Another connection is currently being queried."); | |||
return rfb::VNCServerST::REJECT; | |||
server->approveConnection(sock, false, "Another connection is currently being queried."); | |||
return; | |||
} | |||
count = vncNotifyQueryConnect(); | |||
if (count == 0) { | |||
*reason = strDup("Unable to query the local user to accept the connection."); | |||
return rfb::VNCServerST::REJECT; | |||
server->approveConnection(sock, false, "Unable to query the local user to accept the connection."); | |||
return; | |||
} | |||
queryConnectAddress.replaceBuf(sock->getPeerAddress()); | |||
@@ -171,8 +179,6 @@ XserverDesktop::queryConnection(network::Socket* sock, | |||
queryConnectSocket = sock; | |||
queryConnectTimer.start(queryConnectTimeout * 1000); | |||
return rfb::VNCServerST::PENDING; | |||
} | |||
void XserverDesktop::bell() |
@@ -34,7 +34,7 @@ | |||
#include <rfb/SDesktop.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/Configuration.h> | |||
#include <rfb/VNCServerST.h> | |||
#include <rfb/Timer.h> | |||
#include <unixcommon.h> | |||
#include "Input.h" | |||
@@ -45,7 +45,6 @@ namespace rfb { | |||
namespace network { class SocketListener; class Socket; class SocketServer; } | |||
class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, | |||
public rfb::VNCServerST::QueryConnectionHandler, | |||
public rfb::Timer::Callback { | |||
public: | |||
@@ -86,6 +85,10 @@ public: | |||
const char* rejectMsg=0); | |||
// rfb::SDesktop callbacks | |||
virtual void start(rfb::VNCServer* vs); | |||
virtual void stop(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
@@ -95,11 +98,6 @@ public: | |||
// rfb::PixelBuffer callbacks | |||
virtual void grabRegion(const rfb::Region& r); | |||
// rfb::VNCServerST::QueryConnectionHandler callback | |||
virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason); | |||
protected: | |||
bool handleListenerEvent(int fd, | |||
std::list<network::SocketListener*>* sockets, | |||
@@ -113,7 +111,7 @@ protected: | |||
private: | |||
int screenIndex; | |||
rfb::VNCServerST* server; | |||
rfb::VNCServer* server; | |||
std::list<network::SocketListener*> listeners; | |||
bool directFbptr; | |||
@@ -20,6 +20,8 @@ | |||
// | |||
// The SDisplay class encapsulates a particular system display. | |||
#include <assert.h> | |||
#include <rfb_win32/SDisplay.h> | |||
#include <rfb_win32/Service.h> | |||
#include <rfb_win32/TsSessions.h> | |||
@@ -66,7 +68,7 @@ SDisplay::SDisplay() | |||
: server(0), pb(0), device(0), | |||
core(0), ptr(0), kbd(0), clipboard(0), | |||
inputs(0), monitor(0), cleanDesktop(0), cursor(0), | |||
statusLocation(0), ledState(0) | |||
statusLocation(0), queryConnectionHandler(0), ledState(0) | |||
{ | |||
updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | |||
} | |||
@@ -139,6 +141,20 @@ void SDisplay::stop() | |||
} | |||
void SDisplay::queryConnection(network::Socket* sock, | |||
const char* userName) | |||
{ | |||
assert(server != NULL); | |||
if (queryConnectionHandler) { | |||
queryConnectionHandler->queryConnection(sock, userName); | |||
return; | |||
} | |||
server->approveConnection(sock, true); | |||
} | |||
void SDisplay::startCore() { | |||
// Currently, we just check whether we're in the console session, and |
@@ -52,6 +52,13 @@ namespace rfb { | |||
virtual const char* methodName() const = 0; | |||
}; | |||
class QueryConnectionHandler { | |||
public: | |||
virtual ~QueryConnectionHandler() {} | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName) = 0; | |||
}; | |||
class SDisplay : public SDesktop, | |||
WMMonitor::Notifier, | |||
Clipboard::Notifier, | |||
@@ -65,6 +72,8 @@ namespace rfb { | |||
virtual void start(VNCServer* vs); | |||
virtual void stop(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
virtual void pointerEvent(const Point& pos, int buttonmask); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
@@ -86,6 +95,12 @@ namespace rfb { | |||
void setStatusLocation(bool* status) {statusLocation = status;} | |||
// -=- Set handler for incoming connections | |||
void setQueryConnectionHandler(QueryConnectionHandler* qch) { | |||
queryConnectionHandler = qch; | |||
} | |||
static IntParameter updateMethod; | |||
static BoolParameter disableLocalInputs; | |||
static StringParameter disconnectAction; | |||
@@ -150,6 +165,9 @@ namespace rfb { | |||
// -=- Where to write the active/inactive indicator to | |||
bool* statusLocation; | |||
// -=- Whom to query incoming connections | |||
QueryConnectionHandler* queryConnectionHandler; | |||
unsigned ledState; | |||
}; | |||
@@ -78,6 +78,7 @@ void SocketManager::addListener(network::SocketListener* sock_, | |||
li.sock = sock_; | |||
li.server = srvr; | |||
li.notifier = acn; | |||
li.disable = false; | |||
listeners[event] = li; | |||
} | |||
@@ -128,6 +129,32 @@ void SocketManager::remSocket(network::Socket* sock_) { | |||
throw rdr::Exception("Socket not registered"); | |||
} | |||
bool SocketManager::getDisable(network::SocketServer* srvr) | |||
{ | |||
std::map<HANDLE,ListenInfo>::iterator i; | |||
for (i=listeners.begin(); i!=listeners.end(); i++) { | |||
if (i->second.server == srvr) { | |||
return i->second.disable; | |||
} | |||
} | |||
throw rdr::Exception("Listener not registered"); | |||
} | |||
void SocketManager::setDisable(network::SocketServer* srvr, bool disable) | |||
{ | |||
bool found = false; | |||
std::map<HANDLE,ListenInfo>::iterator i; | |||
for (i=listeners.begin(); i!=listeners.end(); i++) { | |||
if (i->second.server == srvr) { | |||
i->second.disable = disable; | |||
// There might be multiple sockets for the same server, so | |||
// continue iterating | |||
found = true; | |||
} | |||
} | |||
if (!found) | |||
throw rdr::Exception("Listener not registered"); | |||
} | |||
int SocketManager::checkTimeouts() { | |||
int timeout = EventManager::checkTimeouts(); | |||
@@ -164,7 +191,7 @@ void SocketManager::processEvent(HANDLE event) { | |||
WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events); | |||
if (network_events.lNetworkEvents & FD_ACCEPT) { | |||
network::Socket* new_sock = li.sock->accept(); | |||
if (new_sock && li.server->getDisable()) { | |||
if (new_sock && li.disable) { | |||
delete new_sock; | |||
new_sock = 0; | |||
} |
@@ -65,6 +65,9 @@ namespace rfb { | |||
// the SocketServer. | |||
void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true); | |||
bool getDisable(network::SocketServer* srvr); | |||
void setDisable(network::SocketServer* srvr, bool disable); | |||
protected: | |||
virtual int checkTimeouts(); | |||
virtual void processEvent(HANDLE event); | |||
@@ -78,6 +81,7 @@ namespace rfb { | |||
network::SocketListener* sock; | |||
network::SocketServer* server; | |||
AddressChangeNotifier* notifier; | |||
bool disable; | |||
}; | |||
std::map<HANDLE, ListenInfo> listeners; | |||
std::map<HANDLE, ConnInfo> connections; |
@@ -19,10 +19,9 @@ void ControlPanel::initDialog() | |||
{ | |||
TCHAR *ColumnsStrings[] = { | |||
(TCHAR *) "IP address", | |||
(TCHAR *) "Time connected", | |||
(TCHAR *) "Status" | |||
}; | |||
InitLVColumns(IDC_LIST_CONNECTIONS, handle, 120, 3, ColumnsStrings, | |||
InitLVColumns(IDC_LIST_CONNECTIONS, handle, 120, 2, ColumnsStrings, | |||
LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, | |||
LVS_EX_FULLROWSELECT, LVCFMT_LEFT); | |||
SendCommand(4, -1); | |||
@@ -74,7 +73,7 @@ bool ControlPanel::onCommand(int cmd) | |||
} | |||
void ControlPanel::UpdateListView(rfb::ListConnInfo* LCInfo) | |||
void ControlPanel::UpdateListView(ListConnInfo* LCInfo) | |||
{ | |||
getSelConnInfo(); | |||
DeleteAllLVItem(IDC_LIST_CONNECTIONS, handle); | |||
@@ -85,12 +84,12 @@ void ControlPanel::UpdateListView(rfb::ListConnInfo* LCInfo) | |||
ListConn.Copy(LCInfo); | |||
char* ItemString[3]; | |||
char* ItemString[2]; | |||
int i = 0; | |||
for (ListConn.iBegin(); !ListConn.iEnd(); ListConn.iNext()) { | |||
ListConn.iGetCharInfo(ItemString); | |||
InsertLVItem(IDC_LIST_CONNECTIONS, handle, i, (TCHAR **) ItemString, 3); | |||
InsertLVItem(IDC_LIST_CONNECTIONS, handle, i, (TCHAR **) ItemString, 2); | |||
for (ListSelConn.iBegin(); !ListSelConn.iEnd(); ListSelConn.iNext()) { | |||
if (ListSelConn.iGetConn() == ListConn.iGetConn()) | |||
SelectLVItem(IDC_LIST_CONNECTIONS, handle, i); | |||
@@ -141,6 +140,8 @@ void ControlPanel::SendCommand(DWORD command, int data) | |||
{ | |||
COPYDATASTRUCT copyData; | |||
copyData.dwData = command; | |||
copyData.cbData = 0; | |||
copyData.lpData = 0; | |||
getSelConnInfo(); | |||
if (data != -1) { | |||
ListConnStatus.Copy(&ListSelConn); | |||
@@ -149,8 +150,6 @@ void ControlPanel::SendCommand(DWORD command, int data) | |||
} else { | |||
ListConnStatus.Clear(); | |||
} | |||
copyData.cbData = 0; | |||
copyData.lpData = &ListConnStatus; | |||
SendMessage(m_hSTIcon, WM_COPYDATA, 0, (LPARAM)©Data); | |||
} | |||
@@ -11,10 +11,10 @@ | |||
#include <list> | |||
#include <winvnc/resource.h> | |||
#include <winvnc/ListConnInfo.h> | |||
#include <rfb_win32/Dialog.h> | |||
#include <rfb_win32/ListViewControl.h> | |||
#include <rfb_win32/Win32Util.h> | |||
#include <rfb/ListConnInfo.h> | |||
namespace winvnc { | |||
@@ -27,19 +27,19 @@ namespace winvnc { | |||
virtual bool showDialog(); | |||
virtual void initDialog(); | |||
virtual bool onCommand(int cmd); | |||
void UpdateListView(rfb::ListConnInfo* LCInfo); | |||
void UpdateListView(ListConnInfo* LCInfo); | |||
HWND GetHandle() {return handle;}; | |||
void SendCommand(DWORD command, int data); | |||
~ControlPanel(); | |||
rfb::ListConnInfo ListConnStatus; | |||
ListConnInfo ListConnStatus; | |||
protected: | |||
virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); | |||
void getSelConnInfo(); | |||
HWND m_hSTIcon; | |||
rfb::ListConnInfo ListConn; | |||
rfb::ListConnInfo ListSelConn; | |||
ListConnInfo ListConn; | |||
ListConnInfo ListSelConn; | |||
bool stop_updating; | |||
}; | |||
}; | |||
#endif | |||
#endif |
@@ -24,7 +24,7 @@ | |||
#include <rfb/util.h> | |||
namespace rfb { | |||
namespace winvnc { | |||
struct ListConnInfo { | |||
ListConnInfo() : disableClients(false) {} | |||
@@ -32,7 +32,6 @@ namespace rfb { | |||
void Clear() { | |||
conn.clear(); | |||
IP_address.clear(); | |||
time_conn.clear(); | |||
status.clear(); | |||
} | |||
@@ -41,7 +40,6 @@ namespace rfb { | |||
void iBegin() { | |||
ci = conn.begin(); | |||
Ii = IP_address.begin(); | |||
ti = time_conn.begin(); | |||
si = status.begin(); | |||
} | |||
@@ -50,32 +48,29 @@ namespace rfb { | |||
void iNext() { | |||
ci++; | |||
Ii++; | |||
ti++; | |||
si++; | |||
} | |||
void addInfo(void* Conn, char* IP, char* Time, int Status) { | |||
void addInfo(void* Conn, char* IP, int Status) { | |||
conn.push_back(Conn); | |||
IP_address.push_back(strDup(IP)); | |||
time_conn.push_back(strDup(Time)); | |||
IP_address.push_back(rfb::strDup(IP)); | |||
status.push_back(Status); | |||
} | |||
void iGetCharInfo(char* buf[3]) { | |||
void iGetCharInfo(char* buf[2]) { | |||
buf[0] = *Ii; | |||
buf[1] = *ti; | |||
switch (*si) { | |||
case 0: | |||
buf[2] = strDup("Full control"); | |||
buf[1] = rfb::strDup("Full control"); | |||
break; | |||
case 1: | |||
buf[2] = strDup("View only"); | |||
buf[1] = rfb::strDup("View only"); | |||
break; | |||
case 2: | |||
buf[2] = strDup("Stop updating"); | |||
buf[1] = rfb::strDup("Stop updating"); | |||
break; | |||
default: | |||
buf[2] = strDup("Unknown"); | |||
buf[1] = rfb::strDup("Unknown"); | |||
} | |||
} | |||
@@ -95,9 +90,9 @@ namespace rfb { | |||
} | |||
void iAdd (ListConnInfo* InputList) { | |||
char* buf[3]; | |||
char* buf[2]; | |||
InputList->iGetCharInfo(buf); | |||
addInfo(InputList->iGetConn(), buf[0], buf[1], InputList->iGetStatus()); | |||
addInfo(InputList->iGetConn(), buf[0], InputList->iGetStatus()); | |||
} | |||
void setDisable(bool disable) {disableClients = disable;} | |||
@@ -113,11 +108,9 @@ namespace rfb { | |||
private: | |||
std::list<void*> conn; | |||
std::list<char*> IP_address; | |||
std::list<char*> time_conn; | |||
std::list<int> status; | |||
std::list<void*>::iterator ci; | |||
std::list<char*>::iterator Ii; | |||
std::list<char*>::iterator ti; | |||
std::list<int>::iterator si; | |||
bool disableClients; | |||
}; |
@@ -184,7 +184,7 @@ public: | |||
case 2: | |||
return thread.server.disconnectClients("IPC disconnect") ? 1 : 0; | |||
case 3: | |||
thread.server.setClientsStatus((rfb::ListConnInfo *)command->lpData); | |||
thread.server.setClientsStatus(&CPanel->ListConnStatus); | |||
case 4: | |||
thread.server.getClientsInfo(&LCInfo); | |||
CPanel->UpdateListView(&LCInfo); | |||
@@ -230,7 +230,7 @@ protected: | |||
LaunchProcess vncConnect; | |||
STrayIconThread& thread; | |||
ControlPanel * CPanel; | |||
rfb::ListConnInfo LCInfo; | |||
ListConnInfo LCInfo; | |||
}; | |||
@@ -19,6 +19,7 @@ | |||
// -=- WinVNC Version 4.0 Service-Mode implementation | |||
#include <winvnc/VNCServerService.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb_win32/TsSessions.h> | |||
#include <rfb_win32/ModuleFileName.h> | |||
#include <windows.h> |
@@ -20,6 +20,7 @@ | |||
#include <winvnc/VNCServerWin32.h> | |||
#include <winvnc/resource.h> | |||
#include <winvnc/ListConnInfo.h> | |||
#include <winvnc/STrayIcon.h> | |||
#include <os/Mutex.h> | |||
@@ -71,9 +72,7 @@ VNCServerWin32::VNCServerWin32() | |||
// Initialise the desktop | |||
desktop.setStatusLocation(&isDesktopStarted); | |||
// Initialise the VNC server | |||
vncServer.setQueryConnectionHandler(this); | |||
desktop.setQueryConnectionHandler(this); | |||
// Register the desktop's event to be handled | |||
sockMgr.addEvent(desktop.getUpdateEvent(), &desktop); | |||
@@ -241,27 +240,27 @@ bool VNCServerWin32::addNewClient(const char* client) { | |||
return false; | |||
} | |||
bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) { | |||
bool VNCServerWin32::getClientsInfo(ListConnInfo* LCInfo) { | |||
return queueCommand(GetClientsInfo, LCInfo, 0); | |||
} | |||
bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) { | |||
bool VNCServerWin32::setClientsStatus(ListConnInfo* LCInfo) { | |||
return queueCommand(SetClientsStatus, LCInfo, 0); | |||
} | |||
VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) | |||
void VNCServerWin32::queryConnection(network::Socket* sock, | |||
const char* userName) | |||
{ | |||
if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) | |||
return VNCServerST::ACCEPT; | |||
if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) { | |||
vncServer.approveConnection(sock, true, NULL); | |||
return; | |||
} | |||
if (queryConnectDialog) { | |||
*reason = rfb::strDup("Another connection is currently being queried."); | |||
return VNCServerST::REJECT; | |||
vncServer.approveConnection(sock, false, "Another connection is currently being queried."); | |||
return; | |||
} | |||
queryConnectDialog = new QueryConnectDialog(sock, userName, this); | |||
queryConnectDialog->startDialog(); | |||
return VNCServerST::PENDING; | |||
} | |||
void VNCServerWin32::queryConnectionComplete() { | |||
@@ -309,10 +308,10 @@ void VNCServerWin32::processEvent(HANDLE event_) { | |||
sockMgr.addSocket((network::Socket*)commandData, &vncServer); | |||
break; | |||
case GetClientsInfo: | |||
vncServer.getConnInfo((ListConnInfo*)commandData); | |||
getConnInfo((ListConnInfo*)commandData); | |||
break; | |||
case SetClientsStatus: | |||
vncServer.setConnStatus((ListConnInfo*)commandData); | |||
setConnStatus((ListConnInfo*)commandData); | |||
break; | |||
case QueryConnectionComplete: | |||
@@ -341,3 +340,82 @@ void VNCServerWin32::processEvent(HANDLE event_) { | |||
} | |||
} | |||
void VNCServerWin32::getConnInfo(ListConnInfo * listConn) | |||
{ | |||
std::list<network::Socket*> sockets; | |||
std::list<network::Socket*>::iterator i; | |||
listConn->Clear(); | |||
listConn->setDisable(sockMgr.getDisable(&vncServer)); | |||
vncServer.getSockets(&sockets); | |||
for (i = sockets.begin(); i != sockets.end(); i++) { | |||
rfb::SConnection* conn; | |||
int status; | |||
conn = vncServer.getConnection(*i); | |||
if (!conn) | |||
continue; | |||
if (conn->accessCheck(rfb::SConnection::AccessPtrEvents | | |||
rfb::SConnection::AccessKeyEvents | | |||
rfb::SConnection::AccessView)) | |||
status = 0; | |||
else if (conn->accessCheck(rfb::SConnection::AccessView)) | |||
status = 1; | |||
else | |||
status = 2; | |||
listConn->addInfo((void*)(*i), (*i)->getPeerAddress(), status); | |||
} | |||
} | |||
void VNCServerWin32::setConnStatus(ListConnInfo* listConn) | |||
{ | |||
sockMgr.setDisable(&vncServer, listConn->getDisable()); | |||
if (listConn->Empty()) | |||
return; | |||
for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) { | |||
network::Socket* sock; | |||
rfb::SConnection* conn; | |||
int status; | |||
sock = (network::Socket*)listConn->iGetConn(); | |||
conn = vncServer.getConnection(sock); | |||
if (!conn) | |||
continue; | |||
status = listConn->iGetStatus(); | |||
if (status == 3) { | |||
conn->close(0); | |||
} else { | |||
rfb::SConnection::AccessRights ar; | |||
ar = rfb::SConnection::AccessDefault; | |||
switch (status) { | |||
case 0: | |||
ar |= rfb::SConnection::AccessPtrEvents | | |||
rfb::SConnection::AccessKeyEvents | | |||
rfb::SConnection::AccessView; | |||
break; | |||
case 1: | |||
ar |= rfb::SConnection::AccessView; | |||
ar &= ~(rfb::SConnection::AccessPtrEvents | | |||
rfb::SConnection::AccessKeyEvents); | |||
break; | |||
case 2: | |||
ar &= ~(rfb::SConnection::AccessPtrEvents | | |||
rfb::SConnection::AccessKeyEvents | | |||
rfb::SConnection::AccessView); | |||
break; | |||
} | |||
conn->setAccessRights(ar); | |||
conn->framebufferUpdateRequest(vncServer.getPixelBuffer()->getRect(), false); | |||
} | |||
} | |||
} |
@@ -37,9 +37,10 @@ namespace os { | |||
namespace winvnc { | |||
class ListConnInfo; | |||
class STrayIconThread; | |||
class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler, | |||
class VNCServerWin32 : rfb::win32::QueryConnectionHandler, | |||
rfb::win32::SocketManager::AddressChangeNotifier, | |||
rfb::win32::RegConfig::Callback, | |||
rfb::win32::EventHandler { | |||
@@ -73,17 +74,16 @@ namespace winvnc { | |||
// Where to read the configuration settings from | |||
static const TCHAR* RegConfigPath; | |||
bool getClientsInfo(rfb::ListConnInfo* LCInfo); | |||
bool getClientsInfo(ListConnInfo* LCInfo); | |||
bool setClientsStatus(rfb::ListConnInfo* LCInfo); | |||
bool setClientsStatus(ListConnInfo* LCInfo); | |||
protected: | |||
// VNCServerST::QueryConnectionHandler interface | |||
// QueryConnectionHandler interface | |||
// Callback used to prompt user to accept or reject a connection. | |||
// CALLBACK IN VNCServerST "HOST" THREAD | |||
virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
// SocketManager::AddressChangeNotifier interface | |||
// Used to keep tray icon up to date | |||
@@ -97,6 +97,9 @@ namespace winvnc { | |||
// Used to perform queued commands | |||
virtual void processEvent(HANDLE event); | |||
void getConnInfo(ListConnInfo * listConn); | |||
void setConnStatus(ListConnInfo* listConn); | |||
protected: | |||
// Perform a particular internal function in the server thread | |||
typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete, SetClientsStatus, GetClientsInfo} Command; |