diff options
77 files changed, 748 insertions, 377 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a34badb7..f41f7791 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update @@ -23,7 +23,7 @@ jobs: - name: Install working-directory: build run: make tarball - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: Linux (Ubuntu) path: build/tigervnc-*.tar.gz @@ -35,7 +35,7 @@ jobs: run: shell: msys2 {0} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 - name: Install dependencies run: | @@ -55,7 +55,7 @@ jobs: env: MSYS2_PATH_TYPE: inherit run: make installer winvnc_installer - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: Windows path: build/release/tigervnc*.exe @@ -64,7 +64,7 @@ jobs: runs-on: macos-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: | brew install fltk pixman ffmpeg @@ -77,7 +77,7 @@ jobs: - name: Install working-directory: build run: make dmg - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: macOS path: build/TigerVNC-*.dmg @@ -89,9 +89,9 @@ jobs: matrix: java: [ '8', '11', '16' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} @@ -101,7 +101,7 @@ jobs: - name: Build working-directory: java/build run: make - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: Java (${{ matrix.java }}) path: java/build/VncViewer.jar @@ -122,12 +122,12 @@ jobs: env: DOCKER: ${{ matrix.target }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build image run: docker build -t tigervnc/$DOCKER .github/containers/$DOCKER - name: Build packages run: .github/containers/$DOCKER/build.sh - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: Packages (${{ matrix.target }}) path: .github/containers/${{ matrix.target }}/result @@ -10,3 +10,6 @@ CMakeCache.txt Makefile Makefile.in config.h +cmake_install.cmake +cmake_uninstall.cmake +install_manifest.txt diff --git a/common/network/Socket.h b/common/network/Socket.h index 117851c1..7085d73a 100644 --- a/common/network/Socket.h +++ b/common/network/Socket.h @@ -111,41 +111,6 @@ namespace network { SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {} }; - class SocketServer { - public: - virtual ~SocketServer() {} - - // addSocket() tells the server to serve the Socket. The caller - // retains ownership of the Socket - the only way for the server - // to discard a Socket is by calling shutdown() on it. - // outgoing is set to true if the socket was created by connecting out - // to another host, or false if the socket was created by accept()ing - // an incoming connection. - virtual void addSocket(network::Socket* sock, bool outgoing=false) = 0; - - // removeSocket() tells the server to stop serving the Socket. The - // caller retains ownership of the Socket - the server must NOT - // delete the Socket! This call is used mainly to cause per-Socket - // resources to be freed. - virtual void removeSocket(network::Socket* sock) = 0; - - // getSockets() gets a list of sockets. This can be used to generate an - // fd_set for calling select(). - virtual void getSockets(std::list<network::Socket*>* sockets) = 0; - - // processSocketReadEvent() tells the server there is a Socket read event. - // The implementation can indicate that the Socket is no longer active - // by calling shutdown() on it. The caller will then call removeSocket() - // soon after processSocketEvent returns, to allow any pre-Socket - // resources to be tidied up. - virtual void processSocketReadEvent(network::Socket* sock) = 0; - - // processSocketReadEvent() tells the server there is a Socket write event. - // This is only necessary if the Socket has been put in non-blocking - // mode and needs this callback to flush the buffer. - virtual void processSocketWriteEvent(network::Socket* sock) = 0; - }; - } #endif // __NETWORK_SOCKET_H__ diff --git a/common/os/os.cxx b/common/os/os.cxx index 2dfabc46..83995d0d 100644 --- a/common/os/os.cxx +++ b/common/os/os.cxx @@ -24,6 +24,9 @@ #include <os/os.h> #include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> #ifndef WIN32 #include <pwd.h> @@ -31,20 +34,22 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> #include <unistd.h> #else #include <windows.h> #include <wininet.h> /* MinGW needs it */ #include <shlobj.h> +#define stat _stat +#define mkdir(path, mode) mkdir(path) #endif -static const char* gethomedir(bool userDir) +static const char* getvncdir(bool userDir, const char *xdg_env, const char *xdg_def) { - static char dir[PATH_MAX]; + static char dir[PATH_MAX], legacy[PATH_MAX]; + struct stat st; #ifndef WIN32 - char *homedir; + char *homedir, *xdgdir; uid_t uid; struct passwd *passwd; #else @@ -66,10 +71,17 @@ static const char* gethomedir(bool userDir) if (userDir) return homedir; - snprintf(dir, sizeof(dir), "%s/.vnc", homedir); + xdgdir = getenv(xdg_env); + if (xdgdir != NULL && xdgdir[0] == '/') + snprintf(dir, sizeof(dir), "%s/tigervnc", xdgdir); + else + snprintf(dir, sizeof(dir), "%s/%s/tigervnc", homedir, xdg_def); - return dir; + snprintf(legacy, sizeof(legacy), "%s/.vnc", homedir); #else + (void) xdg_def; + (void) xdg_env; + if (userDir) ret = SHGetSpecialFolderPath(NULL, dir, CSIDL_PROFILE, FALSE); else @@ -81,22 +93,70 @@ static const char* gethomedir(bool userDir) if (userDir) return dir; - if (strlen(dir) + strlen("\\vnc") >= sizeof(dir)) + ret = SHGetSpecialFolderPath(NULL, legacy, CSIDL_APPDATA, FALSE); + + if (ret == FALSE) return NULL; - strcat(dir, "\\vnc"); + if (strlen(dir) + strlen("\\TigerVNC") >= sizeof(dir)) + return NULL; + if (strlen(legacy) + strlen("\\vnc") >= sizeof(legacy)) + return NULL; - return dir; + strcat(dir, "\\TigerVNC"); + strcat(legacy, "\\vnc"); #endif + return (stat(dir, &st) != 0 && stat(legacy, &st) == 0) ? legacy : dir; } -const char* os::getvnchomedir() +const char* os::getuserhomedir() { - return gethomedir(false); + return getvncdir(true, NULL, NULL); } -const char* os::getuserhomedir() +const char* os::getvncconfigdir() { - return gethomedir(true); + return getvncdir(false, "XDG_CONFIG_HOME", ".config"); } +const char* os::getvncdatadir() +{ + return getvncdir(false, "XDG_DATA_HOME", ".local/share"); +} + +const char* os::getvncstatedir() +{ + return getvncdir(false, "XDG_STATE_HOME", ".local/state"); +} + +int os::mkdir_p(const char *path_, mode_t mode) +{ + char *path = strdup(path_); + char *p; + +#ifdef WIN32 + (void)mode; +#endif + + for (p = path + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if (mkdir(path, mode) == -1) { + if (errno != EEXIST) { + free(path); + return -1; + } + } + *p = '/'; + } + } + + if (mkdir(path, mode) == -1) { + free(path); + return -1; + } + + free(path); + + return 0; +} diff --git a/common/os/os.h b/common/os/os.h index 5f927fef..a3448070 100644 --- a/common/os/os.h +++ b/common/os/os.h @@ -20,26 +20,55 @@ #ifndef OS_OS_H #define OS_OS_H +#include <sys/stat.h> + namespace os { /* - * Get VNC home directory ($HOME/.vnc or %APPDATA%/vnc/). + * Get user home directory. * If HOME environment variable is set then it is used. * Otherwise home directory is obtained via getpwuid function. * * Returns NULL on failure. */ - const char* getvnchomedir(); + const char* getuserhomedir(); /* - * Get user home directory. - * If HOME environment variable is set then it is used. - * Otherwise home directory is obtained via getpwuid function. + * Get VNC config directory. On Unix-like systems, this is either: + * - $XDG_CONFIG_HOME/tigervnc + * - $HOME/.config/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. * * Returns NULL on failure. */ - const char* getuserhomedir(); + const char* getvncconfigdir(); + /* + * Get VNC data directory used for X.509 known hosts. + * On Unix-like systems, this is either: + * - $XDG_DATA_HOME/tigervnc + * - $HOME/.local/share/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. + * + * Returns NULL on failure. + */ + const char* getvncdatadir(); + + /* + * Get VNC state (logs) directory. On Unix-like systems, this is either: + * - $XDG_STATE_HOME/tigervnc + * - $HOME/.local/state/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. + * + * Returns NULL on failure. + */ + const char* getvncstatedir(); + + /* + * Create directory recursively. Useful to create the nested directory + * structures needed for the above directories. + */ + int mkdir_p(const char *path, mode_t mode); } #endif /* OS_OS_H */ diff --git a/common/rfb/AccessRights.cxx b/common/rfb/AccessRights.cxx new file mode 100644 index 00000000..65e6ce24 --- /dev/null +++ b/common/rfb/AccessRights.cxx @@ -0,0 +1,36 @@ +/* Copyright 2024 TigerVNC Team + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "AccessRights.h" + +namespace rfb +{ + + // AccessRights values + const AccessRights AccessNone = 0x0000; + const AccessRights AccessView = 0x0001; + const AccessRights AccessKeyEvents = 0x0002; + const AccessRights AccessPtrEvents = 0x0004; + const AccessRights AccessCutText = 0x0008; + const AccessRights AccessSetDesktopSize = 0x0010; + const AccessRights AccessNonShared = 0x0020; + const AccessRights AccessDefault = 0x03ff; + const AccessRights AccessNoQuery = 0x0400; + const AccessRights AccessFull = 0xffff; + +} /* namespace rfb */ diff --git a/common/rfb/AccessRights.h b/common/rfb/AccessRights.h new file mode 100644 index 00000000..adf4393d --- /dev/null +++ b/common/rfb/AccessRights.h @@ -0,0 +1,41 @@ +/* Copyright 2024 TigerVNC Team + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef COMMON_RFB_ACCESSRIGHTS_H_ +#define COMMON_RFB_ACCESSRIGHTS_H_ + +#include <stdint.h> + +namespace rfb +{ + + typedef uint16_t AccessRights; + extern const AccessRights AccessNone; // No rights at all + extern const AccessRights AccessView; // View display contents + extern const AccessRights AccessKeyEvents; // Send key events + extern const AccessRights AccessPtrEvents; // Send pointer events + extern const AccessRights AccessCutText; // Send/receive clipboard events + extern const AccessRights AccessSetDesktopSize; // Change desktop size + extern const AccessRights AccessNonShared; // Exclusive access to the server + extern const AccessRights AccessDefault; // The default rights, INCLUDING FUTURE ONES + extern const AccessRights AccessNoQuery; // Connect without local user accepting + extern const AccessRights AccessFull; // All of the available AND FUTURE rights + +} /* namespace rfb */ + +#endif /* COMMON_RFB_ACCESSRIGHTS_H_ */ diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 2cae2356..360434a9 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(rfb STATIC + AccessRights.cxx Blacklist.cxx Congestion.cxx CConnection.cxx diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx index 90540959..11e6dfe3 100644 --- a/common/rfb/CSecurityTLS.cxx +++ b/common/rfb/CSecurityTLS.cxx @@ -58,28 +58,27 @@ using namespace rfb; -static const char* homedirfn(const char* fn); +static const char* configdirfn(const char* fn); StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate", - homedirfn("x509_ca.pem"), + configdirfn("x509_ca.pem"), ConfViewer); StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file", - homedirfn("x509_crl.pem"), + configdirfn("x509_crl.pem"), ConfViewer); static LogWriter vlog("TLS"); -static const char* homedirfn(const char* fn) +static const char* configdirfn(const char* fn) { static char full_path[PATH_MAX]; - const char* homedir; + const char* configdir; - homedir = os::getvnchomedir(); - if (homedir == NULL) + configdir = os::getvncconfigdir(); + if (configdir == NULL) return ""; - snprintf(full_path, sizeof(full_path), "%s/%s", homedir, fn); - + snprintf(full_path, sizeof(full_path), "%s/%s", configdir, fn); return full_path; } @@ -308,7 +307,7 @@ void CSecurityTLS::checkSession() int err; bool hostname_match; - const char *homeDir; + const char *hostsDir; gnutls_datum_t info; size_t len; @@ -385,14 +384,14 @@ void CSecurityTLS::checkSession() /* Certificate has some user overridable problems, so TOFU time */ - homeDir = os::getvnchomedir(); - if (homeDir == NULL) { - throw AuthFailureException("Could not obtain VNC home directory " + hostsDir = os::getvncstatedir(); + if (hostsDir == NULL) { + throw AuthFailureException("Could not obtain VNC state directory " "path for known hosts storage"); } std::string dbPath; - dbPath = (std::string)homeDir + "/x509_known_hosts"; + dbPath = (std::string)hostsDir + "/x509_known_hosts"; err = gnutls_verify_stored_pubkey(dbPath.c_str(), NULL, client->getServerName(), NULL, diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 402b0c04..12ba0f1a 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -43,24 +43,12 @@ using namespace rfb; static LogWriter vlog("SConnection"); -// AccessRights values -const SConnection::AccessRights SConnection::AccessView = 0x0001; -const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002; -const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004; -const SConnection::AccessRights SConnection::AccessCutText = 0x0008; -const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010; -const SConnection::AccessRights SConnection::AccessNonShared = 0x0020; -const SConnection::AccessRights SConnection::AccessDefault = 0x03ff; -const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400; -const SConnection::AccessRights SConnection::AccessFull = 0xffff; - - -SConnection::SConnection() +SConnection::SConnection(AccessRights accessRights) : readyForSetColourMapEntries(false), is(0), os(0), reader_(0), writer_(0), ssecurity(0), authFailureTimer(this, &SConnection::handleAuthFailureTimeout), state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), - accessRights(0x0000), hasRemoteClipboard(false), + accessRights(accessRights), hasRemoteClipboard(false), hasLocalClipboard(false), unsolicitedClipboardAttempt(false) { @@ -254,7 +242,7 @@ bool SConnection::processSecurityMsg() } state_ = RFBSTATE_QUERYING; - setAccessRights(ssecurity->getAccessRights()); + setAccessRights(accessRights & ssecurity->getAccessRights()); queryConnection(ssecurity->getUserName()); // If the connection got approved right away then we can continue diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 0bd6afdb..5bc61677 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -29,6 +29,7 @@ #include <rdr/InStream.h> #include <rdr/OutStream.h> +#include <rfb/AccessRights.h> #include <rfb/SMsgHandler.h> #include <rfb/SecurityServer.h> #include <rfb/Timer.h> @@ -42,7 +43,7 @@ namespace rfb { class SConnection : public SMsgHandler { public: - SConnection(); + SConnection(AccessRights accessRights); virtual ~SConnection(); // Methods to initialise the connection @@ -175,20 +176,12 @@ namespace rfb { // clipboard via handleClipboardRequest(). virtual void sendClipboardData(const char* data); + // getAccessRights() returns the access rights of a SConnection to the server. + AccessRights getAccessRights() { return accessRights; } + // setAccessRights() allows a security package to limit the access rights // of a SConnection to the server. How the access rights are treated // is up to the derived class. - - typedef uint16_t AccessRights; - static const AccessRights AccessView; // View display contents - static const AccessRights AccessKeyEvents; // Send key events - static const AccessRights AccessPtrEvents; // Send pointer events - static const AccessRights AccessCutText; // Send/receive clipboard events - static const AccessRights AccessSetDesktopSize; // Change desktop size - static const AccessRights AccessNonShared; // Exclusive access to the server - 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); virtual bool accessCheck(AccessRights ar) const; diff --git a/common/rfb/SSecurity.h b/common/rfb/SSecurity.h index fbc3de6f..8e296c5a 100644 --- a/common/rfb/SSecurity.h +++ b/common/rfb/SSecurity.h @@ -62,7 +62,7 @@ namespace rfb { // for this security type. virtual const char* getUserName() const = 0; - virtual SConnection::AccessRights getAccessRights() const { return SConnection::AccessDefault; } + virtual AccessRights getAccessRights() const { return AccessDefault; } protected: SConnection* sc; diff --git a/common/rfb/SSecurityRSAAES.cxx b/common/rfb/SSecurityRSAAES.cxx index 2a8dfa3e..cea62644 100644 --- a/common/rfb/SSecurityRSAAES.cxx +++ b/common/rfb/SSecurityRSAAES.cxx @@ -76,7 +76,7 @@ SSecurityRSAAES::SSecurityRSAAES(SConnection* sc, uint32_t _secType, keySize(_keySize), isAllEncrypted(_isAllEncrypted), secType(_secType), serverKey(), clientKey(), serverKeyN(NULL), serverKeyE(NULL), clientKeyN(NULL), clientKeyE(NULL), - accessRights(SConnection::AccessDefault), + accessRights(AccessDefault), rais(NULL), raos(NULL), rawis(NULL), rawos(NULL) { assert(keySize == 128 || keySize == 256); @@ -578,12 +578,12 @@ void SSecurityRSAAES::verifyPass() throw AuthFailureException("No password configured for VNC Auth"); if (password == passwd) { - accessRights = SConnection::AccessDefault; + accessRights = AccessDefault; return; } if (!passwdReadOnly.empty() && password == passwdReadOnly) { - accessRights = SConnection::AccessView; + accessRights = AccessView; return; } diff --git a/common/rfb/SSecurityRSAAES.h b/common/rfb/SSecurityRSAAES.h index eaeb13a1..0c4fc852 100644 --- a/common/rfb/SSecurityRSAAES.h +++ b/common/rfb/SSecurityRSAAES.h @@ -39,7 +39,7 @@ namespace rfb { virtual bool processMsg(); virtual const char* getUserName() const; virtual int getType() const { return secType; } - virtual SConnection::AccessRights getAccessRights() const + virtual AccessRights getAccessRights() const { return accessRights; } @@ -82,7 +82,7 @@ namespace rfb { char username[256]; char password[256]; - SConnection::AccessRights accessRights; + AccessRights accessRights; rdr::InStream* rais; rdr::OutStream* raos; diff --git a/common/rfb/SSecurityStack.cxx b/common/rfb/SSecurityStack.cxx index 8b1c2a47..9c0321d4 100644 --- a/common/rfb/SSecurityStack.cxx +++ b/common/rfb/SSecurityStack.cxx @@ -71,14 +71,14 @@ const char* SSecurityStack::getUserName() const return c; } -SConnection::AccessRights SSecurityStack::getAccessRights() const +AccessRights SSecurityStack::getAccessRights() const { - SConnection::AccessRights accessRights; + AccessRights accessRights; if (!state0 && !state1) return SSecurity::getAccessRights(); - accessRights = SConnection::AccessFull; + accessRights = AccessFull; if (state0) accessRights &= state0->getAccessRights(); diff --git a/common/rfb/SSecurityStack.h b/common/rfb/SSecurityStack.h index 8b412bdf..cf7b10d0 100644 --- a/common/rfb/SSecurityStack.h +++ b/common/rfb/SSecurityStack.h @@ -32,7 +32,7 @@ namespace rfb { virtual bool processMsg(); virtual int getType() const { return type; }; virtual const char* getUserName() const; - virtual SConnection::AccessRights getAccessRights() const; + virtual AccessRights getAccessRights() const; protected: short state; SSecurity* state0; diff --git a/common/rfb/SSecurityVeNCrypt.cxx b/common/rfb/SSecurityVeNCrypt.cxx index c126d82f..2813f299 100644 --- a/common/rfb/SSecurityVeNCrypt.cxx +++ b/common/rfb/SSecurityVeNCrypt.cxx @@ -180,7 +180,7 @@ const char* SSecurityVeNCrypt::getUserName() const return ssecurity->getUserName(); } -SConnection::AccessRights SSecurityVeNCrypt::getAccessRights() const +AccessRights SSecurityVeNCrypt::getAccessRights() const { if (ssecurity == NULL) return SSecurity::getAccessRights(); diff --git a/common/rfb/SSecurityVeNCrypt.h b/common/rfb/SSecurityVeNCrypt.h index 86cf420a..91713f89 100644 --- a/common/rfb/SSecurityVeNCrypt.h +++ b/common/rfb/SSecurityVeNCrypt.h @@ -37,7 +37,7 @@ namespace rfb { virtual bool processMsg(); virtual int getType() const { return chosenType; } virtual const char* getUserName() const; - virtual SConnection::AccessRights getAccessRights() const; + virtual AccessRights getAccessRights() const; protected: SSecurity *ssecurity; diff --git a/common/rfb/SSecurityVncAuth.cxx b/common/rfb/SSecurityVncAuth.cxx index cbd0ccd2..c1ef1f1c 100644 --- a/common/rfb/SSecurityVncAuth.cxx +++ b/common/rfb/SSecurityVncAuth.cxx @@ -54,7 +54,7 @@ VncAuthPasswdParameter SSecurityVncAuth::vncAuthPasswd SSecurityVncAuth::SSecurityVncAuth(SConnection* sc) : SSecurity(sc), sentChallenge(false), - pg(&vncAuthPasswd), accessRights(0) + pg(&vncAuthPasswd), accessRights(AccessNone) { } @@ -103,13 +103,13 @@ bool SSecurityVncAuth::processMsg() throw AuthFailureException("No password configured for VNC Auth"); if (verifyResponse(passwd.c_str())) { - accessRights = SConnection::AccessDefault; + accessRights = AccessDefault; return true; } if (!passwdReadOnly.empty() && verifyResponse(passwdReadOnly.c_str())) { - accessRights = SConnection::AccessView; + accessRights = AccessView; return true; } diff --git a/common/rfb/SSecurityVncAuth.h b/common/rfb/SSecurityVncAuth.h index 2bd27791..7f27b02b 100644 --- a/common/rfb/SSecurityVncAuth.h +++ b/common/rfb/SSecurityVncAuth.h @@ -55,7 +55,7 @@ namespace rfb { virtual bool processMsg(); virtual int getType() const {return secTypeVncAuth;} virtual const char* getUserName() const {return 0;} - virtual SConnection::AccessRights getAccessRights() const { return accessRights; } + virtual AccessRights getAccessRights() const { return accessRights; } static StringParameter vncAuthPasswdFile; static VncAuthPasswdParameter vncAuthPasswd; private: @@ -65,7 +65,7 @@ namespace rfb { uint8_t response[vncAuthChallengeSize]; bool sentChallenge; VncAuthPasswdGetter* pg; - SConnection::AccessRights accessRights; + AccessRights accessRights; }; } #endif diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index ffbf8be7..306bba1d 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -51,8 +51,9 @@ static LogWriter vlog("VNCSConnST"); static Cursor emptyCursor(0, 0, Point(0, 0), NULL); VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, - bool reverse) - : sock(s), reverseConnection(reverse), + bool reverse, AccessRights ar) + : SConnection(ar), + sock(s), reverseConnection(reverse), inProcessMessages(false), pendingSyncFence(false), syncFence(false), fenceFlags(0), fenceDataLen(0), fenceData(NULL), congestionTimer(this), diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 85bfd38f..3a9ec242 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -40,7 +40,8 @@ namespace rfb { class VNCSConnectionST : private SConnection, public Timer::Callback { public: - VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse); + VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse, + AccessRights ar); virtual ~VNCSConnectionST(); // SConnection methods diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index b49dbfe3..3ac9fb94 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -23,17 +23,48 @@ #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 network { class Socket; } + namespace rfb { - class VNCServer : public UpdateTracker, - public network::SocketServer { + class VNCServer : public UpdateTracker { public: + // addSocket() tells the server to serve the Socket. The caller + // retains ownership of the Socket - the only way for the server + // to discard a Socket is by calling shutdown() on it. + // outgoing is set to true if the socket was created by connecting out + // to another host, or false if the socket was created by accept()ing + // an incoming connection. + // accessRights allows to set the access rights to the server. + virtual void addSocket(network::Socket* sock, bool outgoing=false, + AccessRights accessRights = AccessDefault) = 0; + + // removeSocket() tells the server to stop serving the Socket. The + // caller retains ownership of the Socket - the server must NOT + // delete the Socket! This call is used mainly to cause per-Socket + // resources to be freed. + virtual void removeSocket(network::Socket* sock) = 0; + + // getSockets() gets a list of sockets. This can be used to generate an + // fd_set for calling select(). + virtual void getSockets(std::list<network::Socket*>* sockets) = 0; + + // processSocketReadEvent() tells the server there is a Socket read event. + // The implementation can indicate that the Socket is no longer active + // by calling shutdown() on it. The caller will then call removeSocket() + // soon after processSocketEvent returns, to allow any pre-Socket + // resources to be tidied up. + virtual void processSocketReadEvent(network::Socket* sock) = 0; + + // processSocketReadEvent() tells the server there is a Socket write event. + // This is only necessary if the Socket has been put in non-blocking + // mode and needs this callback to flush the buffer. + virtual void processSocketWriteEvent(network::Socket* sock) = 0; + // blockUpdates()/unblockUpdates() tells the server that the pixel buffer // is currently in flux and may not be accessed. The attributes of the // pixel buffer may still be accessed, but not the frame buffer itself. diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 72cf942d..b9579f12 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -55,6 +55,8 @@ #include <assert.h> #include <stdlib.h> +#include <network/Socket.h> + #include <rfb/ComparingUpdateTracker.h> #include <rfb/Exception.h> #include <rfb/KeyRemapper.h> @@ -130,9 +132,9 @@ VNCServerST::~VNCServerST() } -// SocketServer methods +// VNCServer methods -void VNCServerST::addSocket(network::Socket* sock, bool outgoing) +void VNCServerST::addSocket(network::Socket* sock, bool outgoing, AccessRights accessRights) { // - Check the connection isn't black-marked // *** do this in getSecurity instead? @@ -163,7 +165,7 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); disconnectTimer.stop(); - VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); + VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights); clients.push_front(client); client->init(); } @@ -235,8 +237,6 @@ void VNCServerST::processSocketWriteEvent(network::Socket* sock) throw rdr::Exception("invalid Socket in VNCServerST"); } -// VNCServer methods - void VNCServerST::blockUpdates() { blockCounter++; @@ -702,7 +702,7 @@ void VNCServerST::queryConnection(VNCSConnectionST* client, } // - Does the client have the right to bypass the query? - if (client->accessCheck(SConnection::AccessNoQuery)) + if (client->accessCheck(AccessNoQuery)) { approveConnection(client->getSock(), true, NULL); return; @@ -715,7 +715,7 @@ void VNCServerST::clientReady(VNCSConnectionST* client, bool shared) { if (!shared) { if (rfb::Server::disconnectClients && - client->accessCheck(SConnection::AccessNonShared)) { + client->accessCheck(AccessNonShared)) { // - Close all the other connected clients slog.debug("non-shared connection - closing clients"); closeClients("Non-shared connection requested", client->getSock()); diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 3436d333..90c8d753 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -51,12 +51,13 @@ namespace rfb { virtual ~VNCServerST(); - // Methods overridden from SocketServer + // Methods overridden from VNCServer // addSocket // Causes the server to allocate an RFB-protocol management // structure for the socket & initialise it. - virtual void addSocket(network::Socket* sock, bool outgoing=false); + virtual void addSocket(network::Socket* sock, bool outgoing=false, + AccessRights ar=AccessDefault); // removeSocket // Clean up any resources associated with the Socket @@ -76,9 +77,6 @@ namespace rfb { // Flush pending data from the Socket on to the network. virtual void processSocketWriteEvent(network::Socket* sock); - - // Methods overridden from VNCServer - virtual void blockUpdates(); virtual void unblockUpdates(); virtual uint64_t getMsc(); diff --git a/common/rfb/obfuscate.cxx b/common/rfb/obfuscate.cxx index 1f785893..d40e25c3 100644 --- a/common/rfb/obfuscate.cxx +++ b/common/rfb/obfuscate.cxx @@ -56,11 +56,11 @@ std::string rfb::deobfuscate(const uint8_t *data, size_t len) { char buf[9]; - assert(data != NULL); - if (len != 8) throw rdr::Exception("bad obfuscated password length"); + assert(data != NULL); + deskey(d3desObfuscationKey, DE1); des((uint8_t*)data, (uint8_t*)buf); buf[8] = 0; diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx index d1a8cc33..48f59846 100644 --- a/common/rfb/util.cxx +++ b/common/rfb/util.cxx @@ -126,8 +126,8 @@ namespace rfb { bool hexToBin(const char* in, size_t inlen, uint8_t* out, size_t outlen) { - assert(in); - assert(out); + assert(in || inlen == 0); + assert(out || outlen == 0); if (inlen & 1) return false; diff --git a/contrib/packages/rpm/el7/SOURCES/10-libvnc.conf b/contrib/packages/rpm/el7/SOURCES/10-libvnc.conf index a053a7d8..9a69fc7d 100644 --- a/contrib/packages/rpm/el7/SOURCES/10-libvnc.conf +++ b/contrib/packages/rpm/el7/SOURCES/10-libvnc.conf @@ -15,5 +15,5 @@ # Identifier "Screen0 # DefaultDepth 16 # Option "SecurityTypes" "VncAuth" -# Option "PasswordFile" "/root/.vnc/passwd" +# Option "PasswordFile" "/root/.config/tigervnc/passwd" #EndSection diff --git a/contrib/packages/rpm/el8/SOURCES/10-libvnc.conf b/contrib/packages/rpm/el8/SOURCES/10-libvnc.conf index a053a7d8..9a69fc7d 100644 --- a/contrib/packages/rpm/el8/SOURCES/10-libvnc.conf +++ b/contrib/packages/rpm/el8/SOURCES/10-libvnc.conf @@ -15,5 +15,5 @@ # Identifier "Screen0 # DefaultDepth 16 # Option "SecurityTypes" "VncAuth" -# Option "PasswordFile" "/root/.vnc/passwd" +# Option "PasswordFile" "/root/.config/tigervnc/passwd" #EndSection diff --git a/contrib/packages/rpm/el9/SOURCES/10-libvnc.conf b/contrib/packages/rpm/el9/SOURCES/10-libvnc.conf index a053a7d8..9a69fc7d 100644 --- a/contrib/packages/rpm/el9/SOURCES/10-libvnc.conf +++ b/contrib/packages/rpm/el9/SOURCES/10-libvnc.conf @@ -15,5 +15,5 @@ # Identifier "Screen0 # DefaultDepth 16 # Option "SecurityTypes" "VncAuth" -# Option "PasswordFile" "/root/.vnc/passwd" +# Option "PasswordFile" "/root/.config/tigervnc/passwd" #EndSection diff --git a/java/.gitignore b/java/.gitignore new file mode 100644 index 00000000..a99543fd --- /dev/null +++ b/java/.gitignore @@ -0,0 +1,4 @@ +*.class +*.jar +.idea/ +timestamp diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index 7627a2c5..2fd348f7 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -20,7 +20,10 @@ set(JAVA_KEYSTORE_TYPE "jks" CACHE STRING "Type of keystore (Default: \"jks\")") set(JAVA_KEY_ALIAS NOTFOUND CACHE STRING "Alias for the keystore entry used to generate the signature") set(JAVA_STOREPASS NOTFOUND CACHE STRING "Password required to access the keystore") set(JAVA_KEYPASS NOTFOUND CACHE STRING "Password used to protect the private key of the specified keystore entry") +set(JAVA_PKCS11_PROVIDER_CLASS "sun.security.pkcs11.SunPKCS11" CACHE STRING "PKCS11 SecurityProvider class name") +set(JAVA_PKCS11_PROVIDER_ARG NOTFOUND CACHE STRING "Path to the PKCS11 security provider class config file") set(JAVA_TSA_URL NOTFOUND CACHE STRING "URL of Time Stamping Authority (TSA)") +set(JAVA_CERT_CHAIN NOTFOUND CACHE STRING "Path to CA certificate chain file") if(NOT BUILD) STRING(TIMESTAMP BUILD "%Y%m%d" UTC) @@ -166,9 +169,12 @@ add_custom_command(OUTPUT VncViewer.jar -DJAVA_KEYSTORE=${JAVA_KEYSTORE} -DJAVA_KEYSTORE_TYPE=${JAVA_KEYSTORE_TYPE} -DJAVA_STOREPASS=${JAVA_STOREPASS} + -DJAVA_PKCS11_PROVIDER_CLASS=${JAVA_PKCS11_PROVIDER_CLASS} + -DJAVA_PKCS11_PROVIDER_ARG=${JAVA_PKCS11_PROVIDER_ARG} -DJAVA_KEYPASS=${JAVA_KEYPASS} -DJAVA_KEY_ALIAS=${JAVA_KEY_ALIAS} -DJAVA_TSA_URL=${JAVA_TSA_URL} + -DJAVA_CERT_CHAIN=${JAVA_CERT_CHAIN} -P ${SRCDIR}/cmake/SignJar.cmake) add_custom_target(java ALL DEPENDS VncViewer.jar) diff --git a/java/cmake/SignJar.cmake b/java/cmake/SignJar.cmake index 067116d4..cfca1ba2 100644 --- a/java/cmake/SignJar.cmake +++ b/java/cmake/SignJar.cmake @@ -10,8 +10,20 @@ set(KEYTOOL "${Java_PATH}/keytool") set(JARSIGNER "${Java_PATH}/jarsigner") if(JAVA_KEYSTORE) - if((NOT JAVA_STOREPASS) OR (NOT JAVA_KEYPASS) OR (NOT JAVA_KEY_ALIAS)) - message(FATAL_ERROR "When JAVA_KEYSTORE is specified, JAVA_KEY_ALIAS, JAVA_STOREPASS, and JAVA_KEYPASS must also be specified:\n${ERROR}") + if((NOT JAVA_KEYSTORE_TYPE)) + message(FATAL_ERROR "When JAVA_KEYSTORE is specified, JAVA_KEYSTORE_TYPE must also be specified:\n${ERROR}") + endif() + string(TOUPPER "${JAVA_KEYSTORE_TYPE}" JAVA_KEYSTORE_TYPE_STRING) + if(${JAVA_KEYSTORE_TYPE_STRING} MATCHES "PKCS11") + if((NOT JAVA_PKCS11_PROVIDER_ARG) OR (NOT JAVA_STOREPASS) OR (NOT JAVA_KEY_ALIAS)) + message(FATAL_ERROR "When JAVA_KEYSTORE_TYPE is PKCS11, JAVA_STOREPASS, JAVA_PKCS11_PROVIDER_ARG, and JAVA_KEY_ALIAS must also be specified:\n${ERROR}") + endif() + elseif((${JAVA_KEYSTORE_TYPE_STRING} MATCHES "JKS") OR (${JAVA_KEYSTORE_TYPE_STRING} MATCHES "PKCS12")) + if((NOT JAVA_STOREPASS) OR (NOT JAVA_KEYPASS) OR (NOT JAVA_KEY_ALIAS)) + message(FATAL_ERROR "When JAVA_KEYSTORE_TYPE is JKS or PKCS12, JAVA_STOREPASS, JAVA_KEYPASS, and JAVA_KEY_ALIAS must also be specified:\n${ERROR}") + endif() + else() + message(FATAL_ERROR "Unsupported keystore type:\n${ERROR}") endif() else() message(STATUS "Generating self-signed certificate") @@ -44,14 +56,23 @@ else() set(ARGS ${ARGS} -storepass ${JAVA_STOREPASS}) endif() -if(${JAVA_KEYPASS} MATCHES "^:env") - string(REGEX REPLACE "^:env[\t ]+(.*)$" "\\1" JAVA_KEYPASS "${JAVA_KEYPASS}") - set(ARGS ${ARGS} -keypass:env ${JAVA_KEYPASS}) -elseif("${JAVA_KEYPASS}" MATCHES "^:file") - string(REGEX REPLACE "^:file[\t ]+(.*)$" "\\1" JAVA_KEYPASS "${JAVA_KEYPASS}") - set(ARGS ${ARGS} -keypass:file ${JAVA_KEYPASS}) -else() - set(ARGS ${ARGS} -keypass ${JAVA_KEYPASS}) +if(${JAVA_KEYSTORE_TYPE_STRING} MATCHES "PKCS11") + set(ARGS ${ARGS} -providerClass ${JAVA_PKCS11_PROVIDER_CLASS}) + set(ARGS ${ARGS} -providerArg ${JAVA_PKCS11_PROVIDER_ARG}) +elseif((${JAVA_KEYSTORE_TYPE_STRING} MATCHES "JKS") OR (${JAVA_KEYSTORE_TYPE_STRING} MATCHES "PKCS12")) + if(${JAVA_KEYPASS} MATCHES "^:env") + string(REGEX REPLACE "^:env[\t ]+(.*)$" "\\1" JAVA_KEYPASS "${JAVA_KEYPASS}") + set(ARGS ${ARGS} -keypass:env ${JAVA_KEYPASS}) + elseif("${JAVA_KEYPASS}" MATCHES "^:file") + string(REGEX REPLACE "^:file[\t ]+(.*)$" "\\1" JAVA_KEYPASS "${JAVA_KEYPASS}") + set(ARGS ${ARGS} -keypass:file ${JAVA_KEYPASS}) + else() + set(ARGS ${ARGS} -keypass ${JAVA_KEYPASS}) + endif() +endif() + +if(JAVA_CERT_CHAIN) + set(ARGS ${ARGS} -certchain ${JAVA_CERT_CHAIN}) endif() if(JAVA_TSA_URL) diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java index 14a5eb66..39d1f541 100644 --- a/java/com/tigervnc/rfb/CSecurityTLS.java +++ b/java/com/tigervnc/rfb/CSecurityTLS.java @@ -89,13 +89,13 @@ public class CSecurityTLS extends CSecurity { public static String getDefaultCA() { if (UserPreferences.get("viewer", "x509ca") != null) return UserPreferences.get("viewer", "x509ca"); - return FileUtils.getVncHomeDir()+"x509_ca.pem"; + return FileUtils.getVncConfigDir()+"x509_ca.pem"; } public static String getDefaultCRL() { if (UserPreferences.get("viewer", "x509crl") != null) return UserPreferences.get("viewer", "x509crl"); - return FileUtils.getVncHomeDir()+"x509_crl.pem"; + return FileUtils.getVncConfigDir()+"x509_crl.pem"; } public static void setDefaults() @@ -277,12 +277,12 @@ public class CSecurityTLS extends CSecurity { "do you want to continue?")) throw new AuthFailureException("server certificate has expired"); } - File vncDir = new File(FileUtils.getVncHomeDir()); + File vncDir = new File(FileUtils.getVncStateDir()); if (!vncDir.exists()) { try { vncDir.mkdir(); } catch(SecurityException e) { - throw new AuthFailureException("Could not obtain VNC home directory "+ + throw new AuthFailureException("Could not obtain VNC state directory "+ "path for known hosts storage"); } } @@ -356,7 +356,6 @@ public class CSecurityTLS extends CSecurity { private void store_pubkey(File dbPath, String serverName, String pk) { ArrayList<String> lines = new ArrayList<String>(); - File vncDir = new File(FileUtils.getVncHomeDir()); try { if (dbPath.exists()) { FileReader db = new FileReader(dbPath); diff --git a/java/com/tigervnc/vncviewer/FileUtils.java b/java/com/tigervnc/vncviewer/FileUtils.java index af5da3c2..6b82d3e2 100644 --- a/java/com/tigervnc/vncviewer/FileUtils.java +++ b/java/com/tigervnc/vncviewer/FileUtils.java @@ -22,9 +22,11 @@ import javax.swing.filechooser.FileSystemView; import com.tigervnc.rfb.LogWriter; +import java.io.File; + public class FileUtils { - public static final String getHomeDir() { + public static String getHomeDir() { String homeDir = null; try { String os = System.getProperty("os.name"); @@ -56,21 +58,51 @@ public class FileUtils { vlog.error("Cannot access os.name system property:"+e.getMessage()); } - String separator = null; - try { - separator = Character.toString(java.io.File.separatorChar); - } catch(java.security.AccessControlException e) { - vlog.error("Cannot access file.separator system property:"+e.getMessage()); + return homeDir + getFileSeparator(); + } + + public static String getVncDir(String xdgEnv, String xdgDefault) { + File legacyDir = new File(getHomeDir() + ".vnc" + getFileSeparator()); + String os = System.getProperty("os.name"); + + if (os.startsWith("Windows")) { + File newDir = new File(System.getenv("APPDATA") + getFileSeparator() + "TigerVNC" + getFileSeparator()); + if (!newDir.exists()) { + newDir.mkdirs(); + } + File[] existingFiles = legacyDir.listFiles(); + if (existingFiles != null) { + for (File file : existingFiles) { + file.renameTo(new File(newDir.getPath() + file.getName())); + } + legacyDir.delete(); + } + return newDir.getPath(); + } else { + if (legacyDir.exists()) { + vlog.info("WARNING: ~/.vnc is deprecated, please consult 'man vncviewer' for paths to migrate to."); + return legacyDir.getPath(); + } + String xdgBaseDir = System.getenv(xdgEnv); + return (xdgBaseDir != null && xdgBaseDir.startsWith("/")) + ? xdgBaseDir + getFileSeparator() + "tigervnc" + getFileSeparator() + : getHomeDir() + xdgDefault + getFileSeparator() + "tigervnc" + getFileSeparator(); } + } - return homeDir + getFileSeparator(); + public static String getVncConfigDir() { + return getVncDir("XDG_CONFIG_HOME", ".config"); + } + + public static String getVncDataDir() { + return getVncDir("XDG_DATA_HOME", ".local" + getFileSeparator() + "share"); } - public static final String getVncHomeDir() { - return getHomeDir()+".vnc"+getFileSeparator(); + public static String getVncStateDir() { + return getVncDir("XDG_STATE_HOME", ".local" + getFileSeparator() + "state"); } - public static final String getFileSeparator() { + public static String getFileSeparator() { String separator = null; try { separator = Character.toString(java.io.File.separatorChar); diff --git a/java/com/tigervnc/vncviewer/Parameters.java b/java/com/tigervnc/vncviewer/Parameters.java index f08202c0..8c8465cb 100644 --- a/java/com/tigervnc/vncviewer/Parameters.java +++ b/java/com/tigervnc/vncviewer/Parameters.java @@ -325,13 +325,6 @@ public class Parameters { if (filename == null || filename.isEmpty()) { saveToReg(servername); return; - /* - String homeDir = FileUtils.getVncHomeDir(); - if (homeDir == null) - throw new Exception("Failed to read configuration file, "+ - "can't obtain home directory path."); - filepath = homeDir.concat("default.tigervnc"); - */ } else { filepath = filename; } @@ -385,16 +378,7 @@ public class Parameters { String filepath; if (filename == null) { - - return loadFromReg(); - - /* - String homeDir = FileUtils.getVncHomeDir(); - if (homeDir == null) - throw new Exception("Failed to read configuration file, "+ - "can't obtain home directory path."); - filepath = homeDir.concat("default.tigervnc"); - */ + return loadFromReg(); } else { filepath = filename; } diff --git a/java/com/tigervnc/vncviewer/ServerDialog.java b/java/com/tigervnc/vncviewer/ServerDialog.java index aeee0b21..5f75fd3d 100644 --- a/java/com/tigervnc/vncviewer/ServerDialog.java +++ b/java/com/tigervnc/vncviewer/ServerDialog.java @@ -235,7 +235,7 @@ class ServerDialog extends Dialog implements Runnable { private void handleLoad() { String title = "Select a TigerVNC configuration file"; - File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc")); + File dflt = new File(FileUtils.getVncConfigDir().concat("default.tigervnc")); FileNameExtensionFilter filter = new FileNameExtensionFilter("TigerVNC configuration (*.tigervnc)", "tigervnc"); File f = showChooser(title, dflt, filter); @@ -245,9 +245,9 @@ class ServerDialog extends Dialog implements Runnable { private void handleSaveAs() { String title = "Save the TigerVNC configuration to file"; - File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc")); + File dflt = new File(FileUtils.getVncConfigDir().concat("default.tigervnc")); if (!dflt.exists() || !dflt.isFile()) - dflt = new File(FileUtils.getVncHomeDir()); + dflt = new File(FileUtils.getVncConfigDir()); FileNameExtensionFilter filter = new FileNameExtensionFilter("TigerVNC configuration (*.tigervnc)", "tigervnc"); File f = showChooser(title, dflt, filter); diff --git a/java/com/tigervnc/vncviewer/Viewport.java b/java/com/tigervnc/vncviewer/Viewport.java index 2e5e42d6..e12ef2a6 100644 --- a/java/com/tigervnc/vncviewer/Viewport.java +++ b/java/com/tigervnc/vncviewer/Viewport.java @@ -149,11 +149,11 @@ class Viewport extends JPanel implements ActionListener { } static final int[] dotcursor_xpm = { - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, - 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, - 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000, + 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000, + 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; public void setCursor(int width, int height, Point hotspot, @@ -188,23 +188,20 @@ class Viewport extends JPanel implements ActionListener { } } - int cw = (int)Math.floor((float)cursor.getWidth() * scaleRatioX); - int ch = (int)Math.floor((float)cursor.getHeight() * scaleRatioY); - - int x = (int)Math.floor((float)cursorHotspot.x * scaleRatioX); - int y = (int)Math.floor((float)cursorHotspot.y * scaleRatioY); - + int cw = (int) Math.floor((float) cursor.getWidth() * scaleRatioX); + int ch = (int) Math.floor((float) cursor.getHeight() * scaleRatioY); + int x = cursorHotspot.x; + int y = cursorHotspot.y; Dimension cs = tk.getBestCursorSize(cw, ch); - if (cs.width != cw && cs.height != ch) { - cw = Math.min(cw, cs.width); - ch = Math.min(ch, cs.height); - x = (int)Math.min(x, Math.max(cs.width - 1, 0)); - y = (int)Math.min(y, Math.max(cs.height - 1, 0)); - BufferedImage tmp = - new BufferedImage(cs.width, cs.height, BufferedImage.TYPE_INT_ARGB_PRE); + if (cs.width != cursor.getWidth() || cs.height != cursor.getHeight()) { + cw = VncViewer.os.startsWith("windows") ? Math.min(cw, cs.width) : cs.width; + ch = VncViewer.os.startsWith("windows") ? Math.min(ch, cs.height) : cs.height; + BufferedImage tmp = new BufferedImage(cs.width, cs.height, BufferedImage.TYPE_INT_ARGB_PRE); Graphics2D g2 = tmp.createGraphics(); g2.drawImage(cursor, 0, 0, cw, ch, 0, 0, width, height, null); g2.dispose(); + x = (int) Math.min(Math.floor((float) x * (float) cw / (float) width), cw - 1); + y = (int) Math.min(Math.floor((float) y * (float) ch / (float) height), ch - 1); cursor = tmp; } diff --git a/tests/.gitignore b/tests/.gitignore index 110f2510..bc2748d9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,6 +1,11 @@ conv +convertlf convperf decperf +emulatemb encperf fbperf +gesturehandler hostport +pixelformat +unicode diff --git a/tests/perf/encperf.cxx b/tests/perf/encperf.cxx index 40e3abfc..388dcc95 100644 --- a/tests/perf/encperf.cxx +++ b/tests/perf/encperf.cxx @@ -41,6 +41,8 @@ #include <rdr/OutStream.h> #include <rdr/FileInStream.h> +#include <rfb/AccessRights.h> + #include <rfb/PixelFormat.h> #include <rfb/CConnection.h> @@ -134,7 +136,7 @@ public: void getStats(double&, unsigned long long&, unsigned long long&); - virtual void setAccessRights(AccessRights ar); + virtual void setAccessRights(rfb::AccessRights ar); virtual void setDesktopSize(int fb_width, int fb_height, const rfb::ScreenSet& layout); @@ -303,6 +305,7 @@ void Manager::getStats(double& ratio, unsigned long long& encodedBytes, } SConn::SConn() +: SConnection(rfb::AccessDefault) { out = new DummyOutStream; setStreams(NULL, out); @@ -329,7 +332,7 @@ void SConn::getStats(double& ratio, unsigned long long& bytes, manager->getStats(ratio, bytes, rawEquivalent); } -void SConn::setAccessRights(AccessRights) +void SConn::setAccessRights(rfb::AccessRights) { } diff --git a/unix/vncconfig/vncExt.c b/unix/vncconfig/vncExt.c index f19123b8..4ec671b8 100644 --- a/unix/vncconfig/vncExt.c +++ b/unix/vncconfig/vncExt.c @@ -228,7 +228,7 @@ Bool XVncExtSelectInput(Display* dpy, Window w, int mask) return True; } -Bool XVncExtConnect(Display* dpy, const char* hostAndPort) +Bool XVncExtConnect(Display* dpy, const char* hostAndPort, Bool viewOnly) { xVncExtConnectReq* req; xVncExtConnectReply rep; @@ -243,6 +243,7 @@ Bool XVncExtConnect(Display* dpy, const char* hostAndPort) req->vncExtReqType = X_VncExtConnect; req->length += (strLen + 3) >> 2; req->strLen = strLen; + req->viewOnly = (CARD8)viewOnly; Data(dpy, hostAndPort, strLen); if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) { UnlockDisplay(dpy); diff --git a/unix/vncconfig/vncExt.h b/unix/vncconfig/vncExt.h index 2b24469e..4383248c 100644 --- a/unix/vncconfig/vncExt.h +++ b/unix/vncconfig/vncExt.h @@ -46,7 +46,7 @@ char* XVncExtGetParamDesc(Display* dpy, const char* param); char** XVncExtListParams(Display* dpy, int* nParams); void XVncExtFreeParamList(char** list); Bool XVncExtSelectInput(Display* dpy, Window w, int mask); -Bool XVncExtConnect(Display* dpy, const char* hostAndPort); +Bool XVncExtConnect(Display* dpy, const char* hostAndPort, Bool viewOnly); Bool XVncExtGetQueryConnect(Display* dpy, char** addr, char** user, int* timeout, void** opaqueId); Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve); @@ -181,7 +181,7 @@ typedef struct { CARD8 vncExtReqType; /* always VncExtConnect */ CARD16 length B16; CARD8 strLen; - CARD8 pad0; + CARD8 viewOnly; CARD16 pad1 B16; } xVncExtConnectReq; #define sz_xVncExtConnectReq 8 diff --git a/unix/vncconfig/vncconfig.cxx b/unix/vncconfig/vncconfig.cxx index be8c8195..30d04ca6 100644 --- a/unix/vncconfig/vncconfig.cxx +++ b/unix/vncconfig/vncconfig.cxx @@ -177,8 +177,8 @@ static void usage() { fprintf(stderr,"usage: %s [parameters]\n", programName); - fprintf(stderr," %s [parameters] -connect <host>[:<port>]\n", - programName); + fprintf(stderr," %s [parameters] -connect " + "[-view-only] <host>[:<port>]\n", programName); fprintf(stderr," %s [parameters] -disconnect\n", programName); fprintf(stderr," %s [parameters] [-set] <Xvnc-param>=<value> ...\n", programName); @@ -240,13 +240,18 @@ int main(int argc, char** argv) if (i < argc) { for (; i < argc; i++) { if (strcmp(argv[i], "-connect") == 0) { + Bool viewOnly = False; i++; + if (strcmp(argv[i], "-view-only") == 0) { + viewOnly = True; + i++; + } if (i >= argc) usage(); - if (!XVncExtConnect(dpy, argv[i])) { + if (!XVncExtConnect(dpy, argv[i], viewOnly)) { fprintf(stderr,"connecting to %s failed\n",argv[i]); } } else if (strcmp(argv[i], "-disconnect") == 0) { - if (!XVncExtConnect(dpy, "")) { + if (!XVncExtConnect(dpy, "", False)) { fprintf(stderr,"disconnecting all clients failed\n"); } } else if (strcmp(argv[i], "-get") == 0) { diff --git a/unix/vncconfig/vncconfig.man b/unix/vncconfig/vncconfig.man index ed9ddda4..b07c02f4 100644 --- a/unix/vncconfig/vncconfig.man +++ b/unix/vncconfig/vncconfig.man @@ -7,7 +7,7 @@ vncconfig \- configure and control a VNC server .br .B vncconfig .RI [ parameters ] -.B \-connect +.B \-connect \fP[\fB-view-only\fP] .IR host [: port ] .br .B vncconfig @@ -55,12 +55,13 @@ is no VNC extension. .SH OPTIONS .TP -.B \-connect \fIhost\fP[:\fIport\fP] +.B \-connect \fP[\fB-view-only\fP] \fIhost\fP[:\fIport\fP] Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer (normally connections are made the other way round - the viewer connects to the server). \fIhost\fP is the host where the listening viewer is running. If it's not listening on the default port of 5500, you can specify \fIhost:port\fP -instead. +instead. The \fB-view-only\fP option specifies that the server must ignore all +keyboard or mouse events sent by the client. . .TP .B \-disconnect diff --git a/unix/vncpasswd/vncpasswd.cxx b/unix/vncpasswd/vncpasswd.cxx index 93ad6f6f..30091a3d 100644 --- a/unix/vncpasswd/vncpasswd.cxx +++ b/unix/vncpasswd/vncpasswd.cxx @@ -23,6 +23,7 @@ #include <config.h> #endif +#include <errno.h> #include <limits.h> #include <stdio.h> #include <string.h> @@ -156,13 +157,16 @@ int main(int argc, char** argv) } if (fname[0] == '\0') { - const char *homeDir = os::getvnchomedir(); - if (homeDir == NULL) { - fprintf(stderr, "Can't obtain VNC home directory\n"); + const char *configDir = os::getvncconfigdir(); + if (configDir == NULL) { + fprintf(stderr, "Can't obtain VNC config directory\n"); exit(1); } - mkdir(homeDir, 0777); - snprintf(fname, sizeof(fname), "%s/passwd", homeDir); + if (os::mkdir_p(configDir, 0777) == -1) { + fprintf(stderr, "Could not create VNC config directory: %s\n", strerror(errno)); + exit(1); + } + snprintf(fname, sizeof(fname), "%s/passwd", configDir); } while (true) { diff --git a/unix/vncpasswd/vncpasswd.man b/unix/vncpasswd/vncpasswd.man index c70a425a..71c6461a 100644 --- a/unix/vncpasswd/vncpasswd.man +++ b/unix/vncpasswd/vncpasswd.man @@ -9,11 +9,12 @@ vncpasswd \- change the VNC password .B vncpasswd allows you to set the password used to access VNC desktops. Its default behavior is to prompt for a VNC password and then store an obfuscated version -of this password to \fIpasswd-file\fR (or to $HOME/.vnc/passwd if no password -file is specified.) The \fBvncserver\fP script runs \fBvncpasswd\fP the first -time you start a VNC desktop, and it invokes \fBXvnc\fP with the appropriate -\fB\-rfbauth\fP option. \fBvncviewer\fP can also be given a password file to -use via the \fB\-passwd\fP option. +of this password to \fIpasswd-file\fR (or to +\fI$XDG_CONFIG_HOME/tigervnc/passwd\fP if no passwordfile is specified.) The +\fBvncserver\fP script runs \fBvncpasswd\fP the first time you start a VNC +desktop, and it invokes \fBXvnc\fP with the appropriate \fB\-rfbauth\fP option. +\fBvncviewer\fP can also be given a password file to use via the \fB\-passwd\fP +option. The password must be at least six characters long (unless the \fB\-f\fR command-line option is used-- see below), and only the first eight @@ -38,7 +39,9 @@ character. .SH FILES .TP -$HOME/.vnc/passwd +\fI$XDG_CONFIG_HOME/tigervnc/passwd\fP +.TQ +\fI$HOME/.config/tigervnc/passwd\fP Default location of the VNC password file. .SH SEE ALSO diff --git a/unix/vncserver/.gitignore b/unix/vncserver/.gitignore new file mode 100644 index 00000000..879eae0c --- /dev/null +++ b/unix/vncserver/.gitignore @@ -0,0 +1,5 @@ +vncserver +vncserver@.service +vncsession +vncsession-start +vncsession.man diff --git a/unix/vncserver/HOWTO.md b/unix/vncserver/HOWTO.md index 9f53048e..f422eb2a 100644 --- a/unix/vncserver/HOWTO.md +++ b/unix/vncserver/HOWTO.md @@ -30,7 +30,8 @@ To configure Xvnc parameters, you need to go to the same directory where you did the user mapping and open `vncserver-config-defaults` configuration file. This file is for the default Xvnc configuration and will be applied to every user unless any of the following applies: -* The user has its own configuration in `$HOME/.vnc/config`. +* The user has its own configuration in `$XDG_CONFIG_HOME/tigervnc/config` + or `$HOME/.config/tigervnc/config`. * The same option with different value is configured in  `vncserver-config-mandatory` configuration file, which replaces the  default configuration and has even a higher priority than the per-user @@ -74,10 +75,10 @@ You need to run it as the user who will run the server. ### Note: If you used TigerVNC before with your user and you already created a -password, then you have to make sure the `$HOME/.vnc` folder created by -`vncpasswd` have the correct *SELinux* context. You either can delete -this folder and recreate it again by creating the password one more -time, or alternatively you can run: +password, then you have to make sure the (legacy, if used) `$HOME/.vnc` +folder created by `vncpasswd` has the correct *SELinux* context. You +either can delete this folder and recreate it again by creating the +password one more time, or alternatively you can run: ``` $ restorecon -RFv /home/<USER>/.vnc ``` diff --git a/unix/vncserver/selinux/.gitignore b/unix/vncserver/selinux/.gitignore new file mode 100644 index 00000000..965f4ec2 --- /dev/null +++ b/unix/vncserver/selinux/.gitignore @@ -0,0 +1,2 @@ +*.pp +*.pp.bz2 diff --git a/unix/vncserver/selinux/Makefile b/unix/vncserver/selinux/Makefile index b23f20f6..34869730 100644 --- a/unix/vncserver/selinux/Makefile +++ b/unix/vncserver/selinux/Makefile @@ -13,15 +13,16 @@ DATADIR=$(PREFIX)/share all: vncsession.pp.bz2 %.pp.bz2: %.pp - bzip2 -9 $^ + cat "$^" | bzip2 -9 > "$@" %.pp: %.te make -f $(DATADIR)/selinux/devel/Makefile $@ + rm -rf tmp clean: rm -f *.pp *.pp.bz2 rm -rf tmp -install: vncsession.pp.bz2 +install: mkdir -p $(DESTDIR)$(DATADIR)/selinux/packages/targeted/ install vncsession.pp.bz2 $(DESTDIR)$(DATADIR)/selinux/packages/targeted/vncsession.pp.bz2 diff --git a/unix/vncserver/selinux/vncsession.fc b/unix/vncserver/selinux/vncsession.fc index bc81f8f2..9ad7d0fb 100644 --- a/unix/vncserver/selinux/vncsession.fc +++ b/unix/vncserver/selinux/vncsession.fc @@ -19,6 +19,12 @@ HOME_DIR/\.vnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) /root/\.vnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +HOME_DIR/\.config/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +/root/\.config/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +HOME_DIR/\.local/share/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +/root/\.local/share/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +HOME_DIR/\.local/state/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) +/root/\.local/state/tigervnc(/.*)? gen_context(system_u:object_r:vnc_home_t,s0) /usr/sbin/vncsession -- gen_context(system_u:object_r:vnc_session_exec_t,s0) /usr/libexec/vncsession-start -- gen_context(system_u:object_r:vnc_session_exec_t,s0) diff --git a/unix/vncserver/selinux/vncsession.te b/unix/vncserver/selinux/vncsession.te index 680be8ea..d92f1bda 100644 --- a/unix/vncserver/selinux/vncsession.te +++ b/unix/vncserver/selinux/vncsession.te @@ -37,6 +37,18 @@ allow vnc_session_t self:fifo_file rw_fifo_file_perms; allow vnc_session_t vnc_session_var_run_t:file manage_file_perms; files_pid_filetrans(vnc_session_t, vnc_session_var_run_t, file) +# Allowed to create ~/.local +optional_policy(` + gnome_filetrans_home_content(vnc_session_t) +') +optional_policy(` + gen_require(` + type gconf_home_t; + ') + create_dirs_pattern(vnc_session_t, gconf_home_t, gconf_home_t) +') + +# Manage TigerVNC files (mainly ~/.local/state/*.log) create_dirs_pattern(vnc_session_t, vnc_home_t, vnc_home_t) manage_files_pattern(vnc_session_t, vnc_home_t, vnc_home_t) manage_fifo_files_pattern(vnc_session_t, vnc_home_t, vnc_home_t) @@ -72,13 +84,16 @@ optional_policy(` userdom_spec_domtrans_all_users(vnc_session_t) userdom_signal_all_users(vnc_session_t) - userdom_user_home_dir_filetrans(vnc_session_t, vnc_home_t, dir, ".vnc") - userdom_admin_home_dir_filetrans(vnc_session_t, vnc_home_t, dir, ".vnc") - - # This also affects other tools, e.g. vncpasswd + # Make sure legacy path has correct type gen_require(` attribute userdomain; + type gconf_home_t; ') userdom_admin_home_dir_filetrans(userdomain, vnc_home_t, dir, ".vnc") userdom_user_home_dir_filetrans(userdomain, vnc_home_t, dir, ".vnc") + + gnome_config_filetrans(userdomain, vnc_home_t, dir, "tigervnc") + gnome_data_filetrans(userdomain, vnc_home_t, dir, "tigervnc") + filetrans_pattern(userdomain, gconf_home_t, vnc_home_t, dir, "tigervnc") + filetrans_pattern(vnc_session_t, gconf_home_t, vnc_home_t, dir, "tigervnc") ') diff --git a/unix/vncserver/tigervnc.pam b/unix/vncserver/tigervnc.pam index 0f4cb3a7..dda76c49 100644 --- a/unix/vncserver/tigervnc.pam +++ b/unix/vncserver/tigervnc.pam @@ -1,4 +1,8 @@ #%PAM-1.0 + +# THIS IS AN EXAMPLE CONFIGURATION +# MODIFY AS NEEDED FOR YOUR DISTRIBUTION + # pam_selinux.so close should be the first session rule -session required pam_selinux.so close session required pam_loginuid.so diff --git a/unix/vncserver/vncserver-config-defaults b/unix/vncserver/vncserver-config-defaults index 81d73886..2dc4314e 100644 --- a/unix/vncserver/vncserver-config-defaults +++ b/unix/vncserver/vncserver-config-defaults @@ -1,7 +1,7 @@ ## Default settings for VNC servers started by the vncserver service # # Any settings given here will override the builtin defaults, but can -# also be overriden by ~/.vnc/config and vncserver-config-mandatory. +# also be overriden by ~/.config/tigervnc/config and vncserver-config-mandatory. # # See HOWTO.md and the following manpages for more details: # vncsession(8) Xvnc(1) diff --git a/unix/vncserver/vncserver-config-mandatory b/unix/vncserver/vncserver-config-mandatory index cfff1bd9..dc716f11 100644 --- a/unix/vncserver/vncserver-config-mandatory +++ b/unix/vncserver/vncserver-config-mandatory @@ -1,7 +1,7 @@ ## Mandatory settings for VNC servers started by the vncserver service # # Any settings given here will override the builtin defaults and -# settings specified in ~/.vnc/config or vnc-config-defaults. +# settings specified in ~/.config/tigervnc/config or vnc-config-defaults. # # See HOWTO.md and the following manpages for more details: # vncsession(8) Xvnc(1) diff --git a/unix/vncserver/vncserver.in b/unix/vncserver/vncserver.in index 95d672b1..ebdd3a97 100755 --- a/unix/vncserver/vncserver.in +++ b/unix/vncserver/vncserver.in @@ -35,7 +35,15 @@ # your site # -$vncUserDir = "$ENV{HOME}/.vnc"; +$vncUserDir = rindex("$ENV{XDG_CONFIG_HOME}", "/", 0) == 0 + ? "$ENV{XDG_CONFIG_HOME}/tigervnc" + : "$ENV{HOME}/.config/tigervnc"; +$vncLegacyDir = "$ENV{HOME}/.vnc"; +if (!stat($vncUserDir) && stat($vncLegacyDir)) { + warn "~/.vnc is deprecated, please consult 'man vncsession' for paths to migrate to."; + $vncUserDir = $vncLegacyDir; +} + $vncUserConfig = "$vncUserDir/config"; $vncSystemConfigDir = "@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc"; diff --git a/unix/vncserver/vncsession.c b/unix/vncserver/vncsession.c index ba131fbd..1ee096c7 100644 --- a/unix/vncserver/vncsession.c +++ b/unix/vncserver/vncsession.c @@ -259,6 +259,19 @@ stop_pam(pam_handle_t * pamh, int pamret) return pamret; } +static char * +getenvp(const char *name, char **envp) +{ + while (*envp) { + size_t varlen; + varlen = strcspn(*envp, "="); + if (strncmp(*envp, name, varlen) == 0) + return *envp + varlen + 1; + envp++; + } + return NULL; +} + static char ** prepare_environ(pam_handle_t * pamh) { @@ -345,13 +358,43 @@ switch_user(const char *username, uid_t uid, gid_t gid) } } +static int +mkdir_p(const char *path_, mode_t mode) +{ + char *path = strdup(path_); + char *p; + + for (p = path + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if (mkdir(path, mode) == -1) { + if (errno != EEXIST) { + free(path); + return -1; + } + } + *p = '/'; + } + } + + if (mkdir(path, mode) == -1) { + free(path); + return -1; + } + + free(path); + + return 0; +} + static void -redir_stdio(const char *homedir, const char *display) +redir_stdio(const char *homedir, const char *display, char **envp) { int fd; long hostlen; - char* hostname = NULL; - char logfile[PATH_MAX]; + char* hostname = NULL, *xdgstate; + char logfile[PATH_MAX], legacy[PATH_MAX]; + struct stat st; fd = open("/dev/null", O_RDONLY); if (fd == -1) { @@ -364,25 +407,37 @@ redir_stdio(const char *homedir, const char *display) } close(fd); - snprintf(logfile, sizeof(logfile), "%s/.vnc", homedir); - if (mkdir(logfile, 0755) == -1) { - if (errno != EEXIST) { - syslog(LOG_CRIT, "Failure creating \"%s\": %s", logfile, strerror(errno)); - _exit(EX_OSERR); - } + xdgstate = getenvp("XDG_STATE_HOME", envp); + if (xdgstate != NULL && xdgstate[0] == '/') + snprintf(logfile, sizeof(logfile), "%s/tigervnc", xdgstate); + else + snprintf(logfile, sizeof(logfile), "%s/.local/state/tigervnc", homedir); + + snprintf(legacy, sizeof(legacy), "%s/.vnc", homedir); + if (stat(logfile, &st) != 0 && stat(legacy, &st) == 0) { + syslog(LOG_WARNING, "~/.vnc is deprecated, please consult 'man vncsession' for paths to migrate to."); + strcpy(logfile, legacy); #ifdef HAVE_SELINUX + /* this is only needed to handle historical type changes for the legacy dir */ int result; - if (selinux_file_context_verify(logfile, 0) == 0) { - result = selinux_restorecon(logfile, SELINUX_RESTORECON_RECURSE); + if (selinux_file_context_verify(legacy, 0) == 0) { + result = selinux_restorecon(legacy, SELINUX_RESTORECON_RECURSE); if (result < 0) { - syslog(LOG_WARNING, "Failure restoring SELinux context for \"%s\": %s", logfile, strerror(errno)); + syslog(LOG_WARNING, "Failure restoring SELinux context for \"%s\": %s", legacy, strerror(errno)); } } #endif } + if (mkdir_p(logfile, 0755) == -1) { + if (errno != EEXIST) { + syslog(LOG_CRIT, "Failure creating \"%s\": %s", logfile, strerror(errno)); + _exit(EX_OSERR); + } + } + hostlen = sysconf(_SC_HOST_NAME_MAX); if (hostlen < 0) { syslog(LOG_CRIT, "sysconf(_SC_HOST_NAME_MAX): %s", strerror(errno)); @@ -395,8 +450,8 @@ redir_stdio(const char *homedir, const char *display) _exit(EX_OSERR); } - snprintf(logfile, sizeof(logfile), "%s/.vnc/%s%s.log", - homedir, hostname, display); + snprintf(logfile + strlen(logfile), sizeof(logfile) - strlen(logfile), "/%s%s.log", + hostname, display); free(hostname); fd = open(logfile, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd == -1) { @@ -422,6 +477,10 @@ close_fds(void) _exit(EX_OSERR); } + // We'll close the file descriptor that the logging uses, so might + // as well do it cleanly + closelog(); + while ((entry = readdir(dir)) != NULL) { int fd; fd = atoi(entry->d_name); @@ -466,7 +525,7 @@ run_script(const char *username, const char *display, char **envp) close_fds(); - redir_stdio(pwent->pw_dir, display); + redir_stdio(pwent->pw_dir, display, envp); // execvpe() is not POSIX and is missing from older glibc // First clear out everything @@ -495,9 +554,12 @@ run_script(const char *username, const char *display, char **envp) child_argv[1] = display; child_argv[2] = NULL; + closelog(); + execvp(child_argv[0], (char*const*)child_argv); // execvp failed + openlog("vncsession", LOG_PID, LOG_AUTH); syslog(LOG_CRIT, "execvp: %s", strerror(errno)); _exit(EX_OSERR); diff --git a/unix/vncserver/vncsession.man.in b/unix/vncserver/vncsession.man.in index d52da10b..a506ae21 100644 --- a/unix/vncserver/vncsession.man.in +++ b/unix/vncserver/vncsession.man.in @@ -27,27 +27,33 @@ debugging in a login shell from a terminal or for running from a terminal as an ordinary user. .SH FILES -Several VNC-related files are found in the directory $HOME/.vnc: +Several VNC-related files are found in the directory \fI$HOME/.config/tigervnc\fP: .TP -@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-defaults -The optional system-wide equivalent of $HOME/.vnc/config. If this file exists -and defines options to be passed to Xvnc, they will be used as defaults for -users. The user's $HOME/.vnc/config overrides settings configured in this file. -The overall configuration file load order is: this file, $HOME/.vnc/config, -and then @CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory. None are required to exist. +\fI@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-defaults\fP +The optional system-wide equivalent of \fI$HOME/.config/tigervnc/config\fP. +If this file exists and defines options to be passed to Xvnc, they will be used +as defaults for users. The user's \fI$HOME/.config/tigervnc/config\fP overrides +settings configured in this file. The overall configuration file load order is: +this file, \fI$HOME/.config/tigervnc/config\fP, and then +\fI@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory\fP. +None are required to exist. .TP -@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory -The optional system-wide equivalent of $HOME/.vnc/config. If this file exists -and defines options to be passed to Xvnc, they will override any of the same -options defined in a user's $HOME/.vnc/config. This file offers a mechanism -to establish some basic form of system-wide policy. WARNING! There is -nothing stopping users from constructing their own vncsession-like script -that calls Xvnc directly to bypass any options defined in -@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory. The overall configuration file load -order is: @CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-defaults, $HOME/.vnc/config, and then -this file. None are required to exist. +\fI@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory\fP +The optional system-wide equivalent of \fI$HOME/.config/tigervnc/config\fP. +If this file exists and defines options to be passed to Xvnc, they will override +any of the same options defined in a user's \fI$HOME/.config/tigervnc/config\fP. +This file offers a mechanism to establish some basic form of system-wide policy. +WARNING! There is nothing stopping users from constructing their own +vncsession-like script that calls Xvnc directly to bypass any options defined in +\fI@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-mandatory\fP. The +overall configuration file load order is: +\fI@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver-config-defaults\fP, +\fI$HOME/.config/tigervnc/config\fP, and then this file. None are required to +exist. .TP -$HOME/.vnc/config +\fI$XDG_CONFIG_HOME/tigervnc/config\fP +.TQ +\fI$HOME/.config/tigervnc/config\fP An optional server config file wherein options to be passed to Xvnc are listed to avoid hard-coding them to the physical invocation. List options in this file one per line. For those requiring an argument, simply separate the option from @@ -61,10 +67,14 @@ can be used to control which session type will be started. This should match one of the files in \fI/usr/share/xsessions\fP. E.g. if there is a file called "gnome.desktop", then "session=gnome" would be set to use that session type. .TP -$HOME/.vnc/passwd +\fI$XDG_CONFIG_HOME/tigervnc/passwd\fP +.TQ +\fI$HOME/.config/tigervnc/passwd\fP The VNC password file. .TP -$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log +\fI$XDG_STATE_HOME/tigervnc/\fIBhost\fI:\fIBdisplay#\fI.log\fP +.TQ +\fI$HOME/.local/state/tigervnc/\fIBhost\fI:\fIBdisplay#\fI.log\fP The log file for Xvnc and the session. .SH SEE ALSO diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx index 1f1f7481..55ea9667 100644 --- a/unix/x0vncserver/XDesktop.cxx +++ b/unix/x0vncserver/XDesktop.cxx @@ -26,6 +26,8 @@ #include <signal.h> #include <unistd.h> +#include <network/Socket.h> + #include <rfb/LogWriter.h> #include <rfb/Exception.h> diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index eaf6f901..d4ee16b8 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -323,7 +323,7 @@ void XserverDesktop::handleSocketEvent(int fd, bool read, bool write) bool XserverDesktop::handleListenerEvent(int fd, std::list<SocketListener*>* sockets, - SocketServer* sockserv) + VNCServer* sockserv) { std::list<SocketListener*>::iterator i; @@ -344,7 +344,7 @@ bool XserverDesktop::handleListenerEvent(int fd, } bool XserverDesktop::handleSocketEvent(int fd, - SocketServer* sockserv, + VNCServer* sockserv, bool read, bool write) { std::list<Socket*> sockets; @@ -414,10 +414,10 @@ void XserverDesktop::blockHandler(int* timeout) } } -void XserverDesktop::addClient(Socket* sock, bool reverse) +void XserverDesktop::addClient(Socket* sock, bool reverse, bool viewOnly) { vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse); - server->addSocket(sock, reverse); + server->addSocket(sock, reverse, viewOnly ? AccessView : AccessDefault); vncSetNotifyFd(sock->getFd(), screenIndex, true, false); } diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index f3c684c7..e604295b 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -42,7 +42,7 @@ namespace rfb { class VNCServerST; } -namespace network { class SocketListener; class Socket; class SocketServer; } +namespace network { class SocketListener; class Socket; } class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, public rfb::Timer::Callback { @@ -75,7 +75,7 @@ public: void add_copied(const rfb::Region &dest, const rfb::Point &delta); void handleSocketEvent(int fd, bool read, bool write); void blockHandler(int* timeout); - void addClient(network::Socket* sock, bool reverse); + void addClient(network::Socket* sock, bool reverse, bool viewOnly); void disconnectClients(); // QueryConnect methods called from X server code @@ -110,9 +110,9 @@ public: protected: bool handleListenerEvent(int fd, std::list<network::SocketListener*>* sockets, - network::SocketServer* sockserv); + rfb::VNCServer* sockserv); bool handleSocketEvent(int fd, - network::SocketServer* sockserv, + rfb::VNCServer* sockserv, bool read, bool write); virtual void handleTimeout(rfb::Timer* t); diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index 075141fa..e43ba150 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -367,7 +367,7 @@ and -once, the Xvnc and associated X clients will die when the user logs out of the X session in the normal way. It is important to use a VNC password in this case. A typical entry in inetd.conf might be: -5951 stream tcp wait james /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.vnc/passwd +5951 stream tcp wait james /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.config/tigervnc/passwd In fact typically, you would have one entry for each user who uses VNC regularly, each of whom has their own dedicated TCP port which they use. In diff --git a/unix/xserver/hw/vnc/vncExt.c b/unix/xserver/hw/vnc/vncExt.c index 89c10550..e98275c2 100644 --- a/unix/xserver/hw/vnc/vncExt.c +++ b/unix/xserver/hw/vnc/vncExt.c @@ -348,7 +348,7 @@ static int ProcVncExtConnect(ClientPtr client) address[stuff->strLen] = 0; rep.success = 0; - if (vncConnectClient(address) == 0) + if (vncConnectClient(address, (int)stuff->viewOnly) == 0) rep.success = 1; rep.type = X_Reply; diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index c0610fe8..4003e768 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -267,7 +267,7 @@ void vncExtensionInit(void) if (scr == 0 && vncInetdSock != -1 && listeners.empty()) { network::Socket* sock = new network::TcpSocket(vncInetdSock); - desktop[scr]->addClient(sock, false); + desktop[scr]->addClient(sock, false, false); vlog.info("added inetd sock"); } } @@ -343,7 +343,7 @@ void vncSendClipboardData(const char* data) desktop[scr]->sendClipboardData(data); } -int vncConnectClient(const char *addr) +int vncConnectClient(const char *addr, int viewOnly) { if (strlen(addr) == 0) { try { @@ -362,7 +362,9 @@ int vncConnectClient(const char *addr) try { network::Socket* sock = new network::TcpSocket(host.c_str(), port); - desktop[0]->addClient(sock, true); + vlog.info("Reverse connection: %s:%d%s", host.c_str(), port, + viewOnly ? " (view only)" : ""); + desktop[0]->addClient(sock, true, (bool)viewOnly); } catch (rdr::Exception& e) { vlog.error("Reverse connection: %s",e.str()); return -1; diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 9ca00b06..6b37fe62 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -57,7 +57,7 @@ void vncRequestClipboard(void); void vncAnnounceClipboard(int available); void vncSendClipboardData(const char* data); -int vncConnectClient(const char *addr); +int vncConnectClient(const char *addr, int viewOnly); void vncGetQueryConnect(uint32_t *opaqueId, const char**username, const char **address, int *timeout); diff --git a/vncviewer/.gitignore b/vncviewer/.gitignore index feba068d..db3cc77d 100644 --- a/vncviewer/.gitignore +++ b/vncviewer/.gitignore @@ -1,3 +1,4 @@ vncviewer vncviewer.desktop.in vncviewer.desktop +org.tigervnc.vncviewer.metainfo.xml diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index 46821de0..82c4af41 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -280,9 +280,7 @@ void CConn::socketEvent(FL_SOCKET fd, void *data) when |= FL_WRITE; Fl::add_fd(fd, when, socketEvent, data); - recursing = false; - Fl::add_fd(fd, FL_READ | FL_EXCEPT, socketEvent, data); } ////////////////////// CConnection callback methods ////////////////////// diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index b85485b2..7d7ae7c3 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -850,11 +850,15 @@ int DesktopWindow::handle(int event) (Fl::event_y() < 0) || (Fl::event_y() >= h())) { ungrabPointer(); } - // We also don't get sensible coordinates on zaphod setups #if !defined(WIN32) && !defined(__APPLE__) - if ((fl_xevent != NULL) && (fl_xevent->type == MotionNotify) && - (((XMotionEvent*)fl_xevent)->root != - XRootWindow(fl_display, fl_screen))) { + Window root, child; + int x, y, wx, wy; + unsigned int mask; + + // We also don't get sensible coordinates on zaphod setups + if (XQueryPointer(fl_display, fl_xid(this), &root, &child, + &x, &y, &wx, &wy, &mask) && + (root != XRootWindow(fl_display, fl_screen))) { ungrabPointer(); } #endif diff --git a/vncviewer/ServerDialog.cxx b/vncviewer/ServerDialog.cxx index 6a766295..4d57b5fb 100644 --- a/vncviewer/ServerDialog.cxx +++ b/vncviewer/ServerDialog.cxx @@ -315,12 +315,12 @@ void ServerDialog::loadServerHistory() return; #endif - const char* homeDir = os::getvnchomedir(); - if (homeDir == NULL) - throw Exception(_("Could not obtain the home directory path")); + const char* stateDir = os::getvncstatedir(); + if (stateDir == NULL) + throw Exception(_("Could not obtain the state directory path")); char filepath[PATH_MAX]; - snprintf(filepath, sizeof(filepath), "%s/%s", homeDir, SERVER_HISTORY); + snprintf(filepath, sizeof(filepath), "%s/%s", stateDir, SERVER_HISTORY); /* Read server history from file */ FILE* f = fopen(filepath, "r"); @@ -381,12 +381,12 @@ void ServerDialog::saveServerHistory() return; #endif - const char* homeDir = os::getvnchomedir(); - if (homeDir == NULL) - throw Exception(_("Could not obtain the home directory path")); + const char* stateDir = os::getvncstatedir(); + if (stateDir == NULL) + throw Exception(_("Could not obtain the state directory path")); char filepath[PATH_MAX]; - snprintf(filepath, sizeof(filepath), "%s/%s", homeDir, SERVER_HISTORY); + snprintf(filepath, sizeof(filepath), "%s/%s", stateDir, SERVER_HISTORY); /* Write server history to file */ FILE* f = fopen(filepath, "w+"); diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index 75d46dca..15ea4ee8 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -629,11 +629,11 @@ void saveViewerParameters(const char *filename, const char *servername) { return; #endif - const char* homeDir = os::getvnchomedir(); - if (homeDir == NULL) - throw Exception(_("Could not obtain the home directory path")); + const char* configDir = os::getvncconfigdir(); + if (configDir == NULL) + throw Exception(_("Could not obtain the config directory path")); - snprintf(filepath, sizeof(filepath), "%s/default.tigervnc", homeDir); + snprintf(filepath, sizeof(filepath), "%s/default.tigervnc", configDir); } else { snprintf(filepath, sizeof(filepath), "%s", filename); } @@ -733,11 +733,11 @@ char* loadViewerParameters(const char *filename) { return loadFromReg(); #endif - const char* homeDir = os::getvnchomedir(); - if (homeDir == NULL) - throw Exception(_("Could not obtain the home directory path")); + const char* configDir = os::getvncconfigdir(); + if (configDir == NULL) + throw Exception(_("Could not obtain the config directory path")); - snprintf(filepath, sizeof(filepath), "%s/default.tigervnc", homeDir); + snprintf(filepath, sizeof(filepath), "%s/default.tigervnc", configDir); } else { snprintf(filepath, sizeof(filepath), "%s", filename); } diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index a5604f76..366327fa 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -37,7 +37,6 @@ #ifdef WIN32 #include <os/winerrno.h> #include <direct.h> -#define mkdir(path, mode) _mkdir(path) #endif #ifdef __APPLE__ @@ -429,19 +428,6 @@ static void init_fltk() #endif } -static void mkvnchomedir() -{ - // Create .vnc in the user's home directory if it doesn't already exist - const char* homeDir = os::getvnchomedir(); - if (homeDir == NULL) { - vlog.error(_("Could not obtain the home directory path")); - } else { - int result = mkdir(homeDir, 0755); - if (result == -1 && errno != EEXIST) - vlog.error(_("Could not create VNC home directory: %s"), strerror(errno)); - } -} - static void usage(const char *programName) { #ifdef WIN32 @@ -728,7 +714,31 @@ int main(int argc, char** argv) migrateDeprecatedOptions(); - mkvnchomedir(); + char *confdir = strdup(os::getvncconfigdir()); +#ifndef WIN32 + char *dotdir = strrchr(confdir, '.'); + if (dotdir != NULL && strcmp(dotdir, ".vnc") == 0) + vlog.info(_("~/.vnc is deprecated, please consult 'man vncviewer' for paths to migrate to.")); +#else + char *vncdir = strrchr(confdir, '\\'); + if (vncdir != NULL && strcmp(vncdir, "vnc") == 0) + vlog.info(_("%%APPDATA%%\\vnc is deprecated, please switch to the %%APPDATA%%\\TigerVNC location.")); +#endif + + if (os::mkdir_p(os::getvncconfigdir(), 0755) == -1) { + if (errno != EEXIST) + vlog.error(_("Could not create VNC config directory: %s"), strerror(errno)); + } + + if (os::mkdir_p(os::getvncdatadir(), 0755) == -1) { + if (errno != EEXIST) + vlog.error(_("Could not create VNC data directory: %s"), strerror(errno)); + } + + if (os::mkdir_p(os::getvncstatedir(), 0755) == -1) { + if (errno != EEXIST) + vlog.error(_("Could not create VNC state directory: %s"), strerror(errno)); + } CSecurity::upg = &dlg; #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man index 928896a1..1a282126 100644 --- a/vncviewer/vncviewer.man +++ b/vncviewer/vncviewer.man @@ -147,19 +147,22 @@ every supported scheme. .B \-passwd, \-PasswordFile \fIpassword-file\fP If you are on a filesystem which gives you access to the password file used by the server, you can specify it here to avoid typing it in. It will usually be -"~/.vnc/passwd". +\fI$XDG_CONFIG_HOME/tigervnc/passwd\fP, or \fI~/.config/tigervnc/passwd\fP +if the former is unset. . .TP .B \-X509CA \fIpath\fP Path to CA certificate to use when authenticating remote servers using any of the X509 security schemes (X509None, X509Vnc, etc.). Must be in PEM -format. Default is \fB$HOME/.vnc/x509_ca.pem\fP. +format. Default is \fI$XDG_CONFIG_HOME/tigervnc/x509_ca.pem\fP, or +\fI~/.config/tigervnc/x509_ca.pem\fP. . .TP .B \-X509CRL \fIpath\fP Path to certificate revocation list to use in conjunction with \fB-X509CA\fP. Must also be in PEM format. Default is -\fB$HOME/.vnc/x509_crl.pem\fP. +\fI$XDG_CONFIG_HOME/tigervnc/x509_crl.pem\fP, or +\fI~/.config/tigervnc/x509_crl.pem\fP. . .TP .B \-Shared @@ -338,17 +341,33 @@ re-connect will be offered. Default is on. .SH FILES .TP -$HOME/.vnc/default.tigervnc +\fI$XDG_CONFIG_HOME/tigervnc/default.tigervnc\fP +.TQ +\fI$HOME/.config/tigervnc/default.tigervnc\fP Default configuration options. This file must have a "magic" first line of "TigerVNC Configuration file Version 1.0" (without quotes), followed by simple <setting>=<value> pairs of your choosing. The available settings are those shown in this man page. .TP -$HOME/.vnc/x509_ca.pem +\fI$XDG_CONFIG_HOME/tigervnc/x509_ca.pem\fP +.TQ +\fI$HOME/.config/tigervnc/x509_ca.pem\fP Default CA certificate for authenticating servers. .TP -$HOME/.vnc/x509_crl.pem +\fI$XDG_CONFIG_HOME/tigervnc/x509_crl.pem\fP +.TQ +\fI$HOME/.config/tigervnc/x509_crl.pem\fP Default certificate revocation list. +.TP +\fI$XDG_DATA_HOME/tigervnc/x509_known_hosts\fP +.TQ +\fI$HOME/.local/share/tigervnc/x509_known_hosts\fP +Known hosts database for certificate-based authentication. +.TP +\fI$XDG_STATE_HOME/tigervnc/tigervnc.history\fP +.TQ +\fI$HOME/.local/state/tigervnc/tigervnc.history\fP +History file for hostnames that have been recently connected to. .SH SEE ALSO .BR Xvnc (1), diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx index c2fba0fb..015ba549 100644 --- a/win/rfb_win32/SocketManager.cxx +++ b/win/rfb_win32/SocketManager.cxx @@ -24,8 +24,12 @@ #include <winsock2.h> #include <list> + +#include <network/Socket.h> + #include <rfb/LogWriter.h> #include <rfb/Timer.h> +#include <rfb/VNCServer.h> #include <rfb/util.h> #include <rfb_win32/SocketManager.h> @@ -55,7 +59,7 @@ static void requestAddressChangeEvents(network::SocketListener* sock_) { void SocketManager::addListener(network::SocketListener* sock_, - network::SocketServer* srvr, + VNCServer* srvr, AddressChangeNotifier* acn) { WSAEVENT event = WSACreateEvent(); long flags = FD_ACCEPT | FD_CLOSE; @@ -103,7 +107,7 @@ void SocketManager::remListener(network::SocketListener* sock) { } -void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) { +void SocketManager::addSocket(network::Socket* sock_, VNCServer* srvr, bool outgoing) { WSAEVENT event = WSACreateEvent(); if (!event || !addEvent(event, this) || (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) { @@ -135,7 +139,7 @@ void SocketManager::remSocket(network::Socket* sock_) { throw rdr::Exception("Socket not registered"); } -bool SocketManager::getDisable(network::SocketServer* srvr) +bool SocketManager::getDisable(VNCServer* srvr) { std::map<HANDLE,ListenInfo>::iterator i; for (i=listeners.begin(); i!=listeners.end(); i++) { @@ -146,7 +150,7 @@ bool SocketManager::getDisable(network::SocketServer* srvr) throw rdr::Exception("Listener not registered"); } -void SocketManager::setDisable(network::SocketServer* srvr, bool disable) +void SocketManager::setDisable(VNCServer* srvr, bool disable) { bool found = false; std::map<HANDLE,ListenInfo>::iterator i; diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h index e5ca02e6..809c470e 100644 --- a/win/rfb_win32/SocketManager.h +++ b/win/rfb_win32/SocketManager.h @@ -19,22 +19,28 @@ // -=- SocketManager.h // Socket manager class for Win32. -// Passed a network::SocketListener and a network::SocketServer when +// Passed a network::SocketListener and a rfb::VNCServer when // constructed. Uses WSAAsyncSelect to get notifications of network // connection attempts. When an incoming connection is received, -// the manager will call network::SocketServer::addClient(). If +// the manager will call rfb::VNCServer::addClient(). If // addClient returns true then the manager registers interest in // network events on that socket, and calls -// network::SocketServer::processSocketEvent(). +// rfb::VNCServer::processSocketEvent(). #ifndef __RFB_WIN32_SOCKET_MGR_H__ #define __RFB_WIN32_SOCKET_MGR_H__ #include <map> -#include <network/Socket.h> #include <rfb_win32/EventManager.h> +namespace network { + class SocketListener; + class Socket; +} + namespace rfb { + class VNCServer; + namespace win32 { class SocketManager : public EventManager, EventHandler { @@ -52,21 +58,21 @@ namespace rfb { }; // Add a listening socket. Incoming connections will be added to the supplied - // SocketServer. + // VNCServer. void addListener(network::SocketListener* sock_, - network::SocketServer* srvr, + VNCServer* srvr, AddressChangeNotifier* acn = 0); // Remove and delete a listening socket. void remListener(network::SocketListener* sock); // Add an already-connected socket. Socket events will cause the supplied - // SocketServer to be called. The socket must ALREADY BE REGISTERED with - // the SocketServer. - void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true); + // VNCServer to be called. The socket must ALREADY BE REGISTERED with + // the VNCServer. + void addSocket(network::Socket* sock_, VNCServer* srvr, bool outgoing=true); - bool getDisable(network::SocketServer* srvr); - void setDisable(network::SocketServer* srvr, bool disable); + bool getDisable(VNCServer* srvr); + void setDisable(VNCServer* srvr, bool disable); protected: virtual int checkTimeouts(); @@ -75,11 +81,11 @@ namespace rfb { struct ConnInfo { network::Socket* sock; - network::SocketServer* server; + VNCServer* server; }; struct ListenInfo { network::SocketListener* sock; - network::SocketServer* server; + VNCServer* server; AddressChangeNotifier* notifier; bool disable; }; diff --git a/win/winvnc/ManagedListener.cxx b/win/winvnc/ManagedListener.cxx index a93ca298..1a278678 100644 --- a/win/winvnc/ManagedListener.cxx +++ b/win/winvnc/ManagedListener.cxx @@ -45,7 +45,7 @@ ManagedListener::~ManagedListener() { } -void ManagedListener::setServer(network::SocketServer* svr) { +void ManagedListener::setServer(rfb::VNCServer* svr) { if (svr == server) return; vlog.info("set server to %p", svr); diff --git a/win/winvnc/ManagedListener.h b/win/winvnc/ManagedListener.h index 39223c79..20503c33 100644 --- a/win/winvnc/ManagedListener.h +++ b/win/winvnc/ManagedListener.h @@ -27,7 +27,7 @@ namespace winvnc { // -=- ManagedListener // Wrapper class which simplifies the management of a listening socket - // on a specified port, attached to a SocketManager and SocketServer. + // on a specified port, attached to a SocketManager and VNCServer. // Reopens sockets & reconfigures filters & callbacks as appropriate. // Handles addition/removal of Listeners from SocketManager internally. @@ -36,7 +36,7 @@ namespace winvnc { ManagedListener(rfb::win32::SocketManager* mgr); ~ManagedListener(); - void setServer(network::SocketServer* svr); + void setServer(rfb::VNCServer* svr); void setPort(int port, bool localOnly=false); void setFilter(const char* filter); void setAddressChangeNotifier(rfb::win32::SocketManager::AddressChangeNotifier* acn); @@ -49,7 +49,7 @@ namespace winvnc { network::TcpFilter* filter; rfb::win32::SocketManager* manager; rfb::win32::SocketManager::AddressChangeNotifier* addrChangeNotifier; - network::SocketServer* server; + rfb::VNCServer* server; int port; bool localOnly; }; diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx index a243d95e..38b2ef16 100644 --- a/win/winvnc/VNCServerWin32.cxx +++ b/win/winvnc/VNCServerWin32.cxx @@ -363,11 +363,11 @@ void VNCServerWin32::getConnInfo(ListConnInfo * listConn) if (!conn->authenticated()) status = 3; - else if (conn->accessCheck(rfb::SConnection::AccessPtrEvents | - rfb::SConnection::AccessKeyEvents | - rfb::SConnection::AccessView)) + else if (conn->accessCheck(rfb::AccessPtrEvents | + rfb::AccessKeyEvents | + rfb::AccessView)) status = 0; - else if (conn->accessCheck(rfb::SConnection::AccessView)) + else if (conn->accessCheck(rfb::AccessView)) status = 1; else status = 2; @@ -398,25 +398,25 @@ void VNCServerWin32::setConnStatus(ListConnInfo* listConn) if (status == 3) { conn->close(0); } else { - rfb::SConnection::AccessRights ar; + rfb::AccessRights ar; - ar = rfb::SConnection::AccessDefault; + ar = rfb::AccessDefault; switch (status) { case 0: - ar |= rfb::SConnection::AccessPtrEvents | - rfb::SConnection::AccessKeyEvents | - rfb::SConnection::AccessView; + ar |= rfb::AccessPtrEvents | + rfb::AccessKeyEvents | + rfb::AccessView; break; case 1: - ar |= rfb::SConnection::AccessView; - ar &= ~(rfb::SConnection::AccessPtrEvents | - rfb::SConnection::AccessKeyEvents); + ar |= rfb::AccessView; + ar &= ~(rfb::AccessPtrEvents | + rfb::AccessKeyEvents); break; case 2: - ar &= ~(rfb::SConnection::AccessPtrEvents | - rfb::SConnection::AccessKeyEvents | - rfb::SConnection::AccessView); + ar &= ~(rfb::AccessPtrEvents | + rfb::AccessKeyEvents | + rfb::AccessView); break; } conn->setAccessRights(ar); |