diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 264 | ||||
-rw-r--r-- | Doxyfile | 16 | ||||
-rw-r--r-- | common/vncauth.c | 5 | ||||
-rw-r--r-- | libvncclient/rfbproto.c | 52 | ||||
-rw-r--r-- | libvncclient/sockets.c | 4 | ||||
-rw-r--r-- | libvncclient/tls_openssl.c | 4 | ||||
-rw-r--r-- | libvncclient/vncviewer.c | 10 | ||||
-rw-r--r-- | libvncserver/httpd.c | 5 | ||||
-rw-r--r-- | libvncserver/main.c | 193 | ||||
l--------- | libvncserver/main.cc | 1 | ||||
-rw-r--r-- | libvncserver/main.h | 60 | ||||
-rw-r--r-- | libvncserver/rfbserver.c | 17 | ||||
-rw-r--r-- | libvncserver/scale.c | 3 | ||||
-rw-r--r-- | libvncserver/sockets.c | 9 | ||||
-rw-r--r-- | libvncserver/websockets.c | 4 | ||||
-rw-r--r-- | rfb/rfb.h | 10 | ||||
-rw-r--r-- | rfb/rfbclient.h | 4 | ||||
-rw-r--r-- | rfb/rfbproto.h | 16 | ||||
-rw-r--r-- | test/copyrecttest.c | 3 | ||||
-rw-r--r-- | test/encodingstest.c | 3 |
21 files changed, 350 insertions, 334 deletions
@@ -40,6 +40,7 @@ Rostislav Lisovy, Oliver Loch, Raphael Kubo da Costa, Amandeep Singh, Brian Bidulock, Daniel Cohen Gindi, David Verbeiren, Luca Falavigna, Matthias Treydte, Nicolas Ruff, Robbert Klarenbeek and Floris Bos. +Modified version for TDE's krfb/krdc applications created and maintained by Timothy Pearson. Probably I forgot quite a few people sending a patch here and there, which really made a difference. Without those, some obscure bugs still would diff --git a/CMakeLists.txt b/CMakeLists.txt index 873cc7b..c6525ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ -cmake_minimum_required(VERSION 3.4) -cmake_policy(SET CMP0037 NEW) +cmake_minimum_required(VERSION 2.6) +if( POLICY CMP0037 ) + cmake_policy(SET CMP0037 NEW) +endif( POLICY CMP0037 ) project(LibVNCServer) include(CheckFunctionExists) @@ -224,12 +226,12 @@ if(LIBVNCSERVER_HAVE_SYS_UIO_H) if(GNUTLS_FOUND) message(STATUS "Building crypto with GnuTLS") set(CRYPTO_LIBRARIES ${GNUTLS_LIBRARIES}) - set(CRYPTO_SOURCES ${COMMON_DIR}/rfbcrypto_gnutls) + set(CRYPTO_SOURCES ${COMMON_DIR}/rfbcrypto_gnutls.c) include_directories(${GNUTLS_INCLUDE_DIR}) elseif(OPENSSL_FOUND) message(STATUS "Building crypto with OpenSSL") set(CRYPTO_LIBRARIES ${OPENSSL_LIBRARIES}) - set(CRYPTO_SOURCES ${COMMON_DIR}/rfbcrypto_openssl) + set(CRYPTO_SOURCES ${COMMON_DIR}/rfbcrypto_openssl.c) else() message(STATUS "Building crypto with builtin functions") set(CRYPTO_SOURCES ${COMMON_DIR}/rfbcrypto_included.c ${COMMON_DIR}/md5.c ${COMMON_DIR}/sha1.c) @@ -309,10 +311,10 @@ endif(WITH_SASL AND LIBSASL2_LIBRARIES AND SASL2_INCLUDE_DIR) # LIBVNCSERVER_ENOENT_WORKAROUND # inline -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rfb/rfbconfig.h.cmakein ${CMAKE_BINARY_DIR}/rfb/rfbconfig.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rfb/rfbconfig.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/rfb/rfbconfig.h) set(LIBVNCSERVER_SOURCES - ${LIBVNCSERVER_DIR}/main.c + ${LIBVNCSERVER_DIR}/main.cc ${LIBVNCSERVER_DIR}/rfbserver.c ${LIBVNCSERVER_DIR}/rfbregion.c ${LIBVNCSERVER_DIR}/auth.c @@ -451,233 +453,33 @@ if(LIBVNCSERVER_WITH_WEBSOCKETS) ) endif(LIBVNCSERVER_WITH_WEBSOCKETS) -add_library(vncclient ${LIBVNCCLIENT_SOURCES}) -add_library(vncserver ${LIBVNCSERVER_SOURCES}) -if(WIN32) - set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} ws2_32) -endif(WIN32) - -target_link_libraries(vncclient - ${ADDITIONAL_LIBS} - ${ZLIB_LIBRARIES} - ${LZO_LIBRARIES} - ${JPEG_LIBRARIES} - ${GNUTLS_LIBRARIES} - ${OPENSSL_LIBRARIES} -) -target_link_libraries(vncserver - ${ADDITIONAL_LIBS} - ${ZLIB_LIBRARIES} - ${LZO_LIBRARIES} - ${JPEG_LIBRARIES} - ${PNG_LIBRARIES} - ${CRYPTO_LIBRARIES} - ${GNUTLS_LIBRARIES} - ${OPENSSL_LIBRARIES} -) - -SET_TARGET_PROPERTIES(vncclient vncserver - PROPERTIES SOVERSION "${VERSION_SO}" VERSION "${PACKAGE_VERSION}" -) - -# EXAMPLES -set(LIBVNCSERVER_EXAMPLES - backchannel - camera - colourmaptest - example - fontsel - pnmshow - pnmshow24 - regiontest - repeater - rotate - simple - simple15 - storepasswd - vncev - ) - -if(CMAKE_USE_PTHREADS_INIT) - set(LIBVNCSERVER_EXAMPLES - ${LIBVNCSERVER_EXAMPLES} - blooptest - ) -endif(CMAKE_USE_PTHREADS_INIT) - -if(WITH_TIGHTVNC_FILETRANSFER AND CMAKE_USE_PTHREADS_INIT) - set(LIBVNCSERVER_EXAMPLES - ${LIBVNCSERVER_EXAMPLES} - filetransfer - ) -endif(WITH_TIGHTVNC_FILETRANSFER AND CMAKE_USE_PTHREADS_INIT) - -if(MACOS) - set(LIBVNCSERVER_EXAMPLES - ${LIBVNCSERVER_EXAMPLES} - mac - ) -endif(MACOS) - -if(ANDROID) - set(LIBVNCSERVER_EXAMPLES - ${LIBVNCSERVER_EXAMPLES} - androidvncserver - ) -endif(ANDROID) - -set(LIBVNCCLIENT_EXAMPLES - backchannel - ppmtest -) - -if(SDL2_FOUND) - include_directories(${SDL2_INCLUDE_DIR}) - set(LIBVNCCLIENT_EXAMPLES - ${LIBVNCCLIENT_EXAMPLES} - SDLvncviewer - ) -endif(SDL2_FOUND) +##### tdevncclient (static) ######################## -if(FFMPEG_FOUND) - set(LIBVNCCLIENT_EXAMPLES - ${LIBVNCCLIENT_EXAMPLES} - vnc2mpg - ) -endif(FFMPEG_FOUND) - - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples) -foreach(e ${LIBVNCSERVER_EXAMPLES}) - add_executable(examples_${e} ${LIBVNCSRVEXAMPLE_DIR}/${e}.c) - set_target_properties(examples_${e} PROPERTIES OUTPUT_NAME ${e}) - set_target_properties(examples_${e} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples) - target_link_libraries(examples_${e} vncserver ${CMAKE_THREAD_LIBS_INIT}) -endforeach(e ${LIBVNCSERVER_EXAMPLES}) - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/client_examples) -foreach(e ${LIBVNCCLIENT_EXAMPLES}) - add_executable(client_examples_${e} ${LIBVNCCLIEXAMPLE_DIR}/${e}.c ${LIBVNCCLIEXAMPLE_DIR}/${${e}_EXTRA_SOURCES} ) - set_target_properties(client_examples_${e} PROPERTIES OUTPUT_NAME ${e}) - set_target_properties(client_examples_${e} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/client_examples) - target_link_libraries(client_examples_${e} vncclient ${CMAKE_THREAD_LIBS_INIT} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES}) -endforeach(e ${LIBVNCCLIENT_EXAMPLES}) - - -# -# them tests -# - -if(UNIX) - set(ADDITIONAL_TEST_LIBS m) -endif(UNIX) - -set(SIMPLETESTS - cargstest - copyrecttest +tde_add_library( tdevncclient STATIC_PIC AUTOMOC + SOURCES + ${LIBVNCCLIENT_SOURCES} + LINK + pthread + ${ADDITIONAL_LIBS} + ${ZLIB_LIBRARIES} + ${LZO_LIBRARIES} + ${JPEG_LIBRARIES} + ${GNUTLS_LIBRARIES} + ${OPENSSL_LIBRARIES} ) -if(CMAKE_USE_PTHREADS_INIT) - set(SIMPLETESTS - ${SIMPLETESTS} - encodingstest - ) -endif(CMAKE_USE_PTHREADS_INIT) - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test) -foreach(t ${SIMPLETESTS}) - add_executable(test_${t} ${TESTS_DIR}/${t}.c) - set_target_properties(test_${t} PROPERTIES OUTPUT_NAME ${t}) - set_target_properties(test_${t} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test) - target_link_libraries(test_${t} vncserver vncclient ${ADDITIONAL_TEST_LIBS}) -endforeach(t ${SIMPLETESTS}) - -if(WITH_JPEG AND FOUND_LIBJPEG_TURBO) - add_executable(test_tjunittest - ${TESTS_DIR}/tjunittest.c - ${TESTS_DIR}/tjutil.c - ${TESTS_DIR}/tjutil.h - ${COMMON_DIR}/turbojpeg.c - ${COMMON_DIR}/turbojpeg.h - ) - set_target_properties(test_tjunittest PROPERTIES OUTPUT_NAME tjunittest) - set_target_properties(test_tjunittest PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test) - target_link_libraries(test_tjunittest vncserver vncclient ${ADDITIONAL_TEST_LIBS}) - - add_executable(test_tjbench - ${TESTS_DIR}/tjbench.c - ${TESTS_DIR}/tjutil.c - ${TESTS_DIR}/tjutil.h - ${TESTS_DIR}/bmp.c - ${TESTS_DIR}/bmp.h - ${COMMON_DIR}/turbojpeg.c - ${COMMON_DIR}/turbojpeg.h - ) - set_target_properties(test_tjbench PROPERTIES OUTPUT_NAME tjbench) - set_target_properties(test_tjbench PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test) - target_link_libraries(test_tjbench vncserver vncclient ${ADDITIONAL_TEST_LIBS}) - -endif(WITH_JPEG AND FOUND_LIBJPEG_TURBO) - -if(LIBVNCSERVER_WITH_WEBSOCKETS) - add_executable(test_wstest - ${TESTS_DIR}/wstest.c - ${TESTS_DIR}/wstestdata.inc - ) - set_target_properties(test_wstest PROPERTIES OUTPUT_NAME wstest) - set_target_properties(test_wstest PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test) - target_link_libraries(test_wstest vncserver vncclient ${ADDITIONAL_TEST_LIBS}) -endif(LIBVNCSERVER_WITH_WEBSOCKETS) +##### tdevncserver (static) ######################## -add_test(NAME cargs COMMAND test_cargstest) -if(FOUND_LIBJPEG_TURBO) - add_test(NAME turbojpeg COMMAND test_tjunittest) -endif(FOUND_LIBJPEG_TURBO) -if(LIBVNCSERVER_WITH_WEBSOCKETS) - add_test(NAME wstest COMMAND test_wstest) -endif(LIBVNCSERVER_WITH_WEBSOCKETS) - -# -# this gets the libraries needed by TARGET in "-libx -liby ..." form -# -function(get_link_libraries OUT TARGET) - set(RESULT "") - get_target_property(LIBRARIES ${TARGET} INTERFACE_LINK_LIBRARIES) - foreach(LIB ${LIBRARIES}) - if("${LIB}" MATCHES ".*NOTFOUND.*") - continue() - endif() - string(REGEX REPLACE "^.*/lib" "" LIB ${LIB}) # remove leading path and "lib" name prefix - string(REGEX REPLACE "-l" "" LIB ${LIB}) # remove leading -l - string(REGEX REPLACE "\\.so$" "" LIB ${LIB}) # remove trailing .so - list(APPEND RESULT "-l${LIB}") - endforeach() - list(REMOVE_DUPLICATES RESULT) - string(CONCAT RESULT ${RESULT}) # back to string - if(RESULT) - string(REPLACE "-l" " -l" RESULT ${RESULT}) # re-add separators - endif(RESULT) - set(${OUT} ${RESULT} PARENT_SCOPE) -endfunction() - -get_link_libraries(PRIVATE_LIBS vncserver) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libvncserver.pc.cmakein ${CMAKE_CURRENT_BINARY_DIR}/libvncserver.pc @ONLY) -get_link_libraries(PRIVATE_LIBS vncclient) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libvncclient.pc.cmakein ${CMAKE_CURRENT_BINARY_DIR}/libvncclient.pc @ONLY) - - -install_targets(/lib vncserver) -install_targets(/lib vncclient) -install_files(/include/rfb FILES - rfb/keysym.h - rfb/rfb.h - rfb/rfbclient.h - rfb/rfbconfig.h - rfb/rfbproto.h - rfb/rfbregion.h -) - -install_files(/lib/pkgconfig FILES - libvncserver.pc - libvncclient.pc +tde_add_library( tdevncserver STATIC_PIC AUTOMOC + SOURCES + ${LIBVNCSERVER_SOURCES} + LINK + ${ADDITIONAL_LIBS} + ${ZLIB_LIBRARIES} + ${LZO_LIBRARIES} + ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${GNUTLS_LIBRARIES} + ${OPENSSL_LIBRARIES} ) @@ -985,7 +985,7 @@ TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a +# that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO @@ -998,25 +998,25 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# http://doc.qt.io/qt-5/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# http://doc.qt.io/qt-5/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# http://doc.qt.io/qt-5/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see -# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# <a href="http://doc.qt.io/qt-5/qthelpproject.html#custom-filters"> # Qt Help Project / Custom Filters</a>. QHP_CUST_FILTER_ATTRS = @@ -1024,13 +1024,13 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. -# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# <a href="http://doc.qt.io/qt-5/qthelpproject.html#filter-attributes"> # Qt Help Project / Filter Attributes</a>. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. +# be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. @@ -1109,7 +1109,7 @@ FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. diff --git a/common/vncauth.c b/common/vncauth.c index 81bb10b..53347d3 100644 --- a/common/vncauth.c +++ b/common/vncauth.c @@ -21,11 +21,6 @@ * vncauth.c - Functions for VNC password management and authentication. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#define _XOPEN_SOURCE 600 -#endif #ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H #include <sys/types.h> #endif diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 2f887c3..267c1c5 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -23,11 +23,6 @@ * rfbproto.c - functions to deal with client side of RFB protocol. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#define _XOPEN_SOURCE 600 -#endif #ifndef WIN32 #include <unistd.h> #include <sys/types.h> @@ -340,6 +335,8 @@ ConnectToRFBServer(rfbClient* client,const char *hostname, int port) /* serverHost is a hostname */ if (!StringToIPAddr(hostname, &host)) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkNameResolutionFailed); rfbClientLog("Couldn't convert '%s' to host address\n", hostname); return FALSE; } @@ -348,6 +345,8 @@ ConnectToRFBServer(rfbClient* client,const char *hostname, int port) } if (client->sock < 0) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkConnectionFailed); rfbClientLog("Unable to connect to VNC server\n"); return FALSE; } @@ -444,6 +443,9 @@ rfbHandleAuthResult(rfbClient* client) authResult = rfbClientSwap32IfLE(authResult); + if (client->AuthenticationResults) + client->AuthenticationResults(client, authResult); + switch (authResult) { case rfbVncAuthOK: rfbClientLog("VNC authentication succeeded\n"); @@ -569,13 +571,19 @@ HandleVncAuth(rfbClient *client) char *passwd=NULL; int i; - if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE; + if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) { + if (client->AuthenticationResults) + client->AuthenticationResults(client, rfbVncAuthFailed); + return FALSE; + } if (client->serverPort!=-1) { /* if not playing a vncrec file */ if (client->GetPassword) passwd = client->GetPassword(client); if ((!passwd) || (strlen(passwd) == 0)) { + if (client->AuthenticationResults) + client->AuthenticationResults(client, rfbVncAuthFailed); rfbClientLog("Reading password failed\n"); return FALSE; } @@ -591,7 +599,11 @@ HandleVncAuth(rfbClient *client) } free(passwd); - if (!WriteToRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE; + if (!WriteToRFBServer(client, (char *)challenge, CHALLENGESIZE)) { + if (client->AuthenticationResults) + client->AuthenticationResults(client, rfbVncAuthFailed); + return FALSE; + } } /* Handle the SecurityResult message */ @@ -1006,7 +1018,11 @@ InitialiseRFBConnection(rfbClient* client) if (client->listenSpecified) errorMessageOnReadFailure = FALSE; - if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg)) return FALSE; + if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg)) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBServerNotValid); + return FALSE; + } pv[sz_rfbProtocolVersionMsg]=0; errorMessageOnReadFailure = TRUE; @@ -1014,6 +1030,8 @@ InitialiseRFBConnection(rfbClient* client) pv[sz_rfbProtocolVersionMsg] = 0; if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBServerNotValid); rfbClientLog("Not a valid VNC server (%s)\n",pv); return FALSE; } @@ -1059,17 +1077,29 @@ InitialiseRFBConnection(rfbClient* client) sprintf(pv,rfbProtocolVersionFormat,client->major,client->minor); - if (!WriteToRFBServer(client, pv, sz_rfbProtocolVersionMsg)) return FALSE; + if (!WriteToRFBServer(client, pv, sz_rfbProtocolVersionMsg)) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBProtocolFailure); + return FALSE; + } /* 3.7 and onwards sends a # of security types first */ if (client->major==3 && client->minor > 6) { - if (!ReadSupportedSecurityType(client, &authScheme, FALSE)) return FALSE; + if (!ReadSupportedSecurityType(client, &authScheme, FALSE)) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBProtocolFailure); + return FALSE; + } } else { - if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) return FALSE; + if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBProtocolFailure); + return FALSE; + } authScheme = rfbClientSwap32IfLE(authScheme); } diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index f042472..2b0ee71 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -187,6 +187,8 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) return FALSE; } } else { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkConnectionClosed); if (errorMessageOnReadFailure) { rfbClientLog("VNC server closed connection\n"); } @@ -230,6 +232,8 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) return FALSE; } } else { + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkConnectionClosed); if (errorMessageOnReadFailure) { rfbClientLog("VNC server closed connection\n"); } diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c index e2fadb2..304116a 100644 --- a/libvncclient/tls_openssl.c +++ b/libvncclient/tls_openssl.c @@ -18,10 +18,6 @@ * USA. */ -#ifndef _MSC_VER -#define _XOPEN_SOURCE 500 -#endif - #include <rfb/rfbclient.h> #include <errno.h> diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index ec1b73a..486390e 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -30,10 +30,6 @@ #define strdup _strdup /* Prevent POSIX deprecation warnings */ #endif -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#endif #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -376,11 +372,17 @@ static rfbBool rfbInitConnection(rfbClient* client) } } + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkConnectionSuccess); + /* Initialise the VNC connection, including reading the password */ if (!InitialiseRFBConnection(client)) return FALSE; + if (client->NetworkStatus) + client->NetworkStatus(client, rfbNetworkRFBConnectionSuccess); + client->width=client->si.framebufferWidth; client->height=client->si.framebufferHeight; if (!client->MallocFrameBuffer(client)) diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index efb97b3..a3ef74d 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -23,11 +23,6 @@ * USA. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#endif - #include <rfb/rfb.h> #include <ctype.h> diff --git a/libvncserver/main.c b/libvncserver/main.c index d3cd9b1..74010d0 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -2,6 +2,7 @@ * This file is called main.c, because it contains most of the new functions * for use with LibVNCServer. * + * Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>. * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de> * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. @@ -10,34 +11,54 @@ * see GPL (latest version) for full details */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#endif -#include <rfb/rfb.h> -#include <rfb/rfbregion.h> -#include "private.h" +extern "C" { + #include <rfb/rfb.h> + #include <rfb/rfbregion.h> + #include "private.h" -#include <stdarg.h> -#include <errno.h> + #include <stdarg.h> + #include <errno.h> -#ifndef false -#define false 0 -#define true -1 -#endif + #ifndef false + #define false 0 + #define true -1 + #endif -#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif + #ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H + #include <sys/types.h> + #endif -#ifndef WIN32 -#include <sys/socket.h> -#include <netinet/in.h> -#include <unistd.h> -#include <fcntl.h> -#endif + #ifndef WIN32 + #include <sys/socket.h> + #include <netinet/in.h> + #include <unistd.h> + #include <fcntl.h> + #endif -#include <signal.h> -#include <time.h> + #include <signal.h> + #include <time.h> +} + +#include <ntqobject.h> +#include <ntqvariant.h> +#include <ntqtimer.h> +#include <ntqthread.h> + +#include "main.h" + +ControlPipeHandlerObject* mControlPipeHandler = NULL; +TQEventLoopThread* mControlPipeHandlerThread = NULL; + +OnHoldClientHandlerObject* mOnHoldClientHandler = NULL; +TQEventLoopThread* mOnHoldClientHandlerThread = NULL; + +extern "C" { + /* from scale.c */ + void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2); + + /* from rfbserver.c */ + rfbClientIteratorPtr rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen); +} static int extMutex_initialized = 0; static int logMutex_initialized = 0; @@ -170,7 +191,7 @@ rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension, if(extData->extension == extension) return FALSE; - extData = calloc(sizeof(rfbExtensionData),1); + extData = (rfbExtensionData*)calloc(sizeof(rfbExtensionData),1); extData->extension = extension; extData->data = data; extData->next = cl->extensions; @@ -463,7 +484,7 @@ clientOutput(void *data) /* Client has disconnected. */ return NULL; } - if (cl->state != RFB_NORMAL || cl->onHold) { + if (cl->state != _rfbClientRec::RFB_NORMAL || cl->onHold) { /* just sleep until things get normal */ usleep(cl->screen->deferUpdateTime * 1000); continue; @@ -519,8 +540,13 @@ static void * clientInput(void *data) { rfbClientPtr cl = (rfbClientPtr)data; - pthread_t output_thread; - pthread_create(&output_thread, NULL, clientOutput, (void *)cl); + /* Start output thread */ + TQEventLoopThread* clientOutputHandlerThread = new TQEventLoopThread(); + ClientOutputHandlerObject* clientOutputHandler = new ClientOutputHandlerObject(); + clientOutputHandler->d = cl; + clientOutputHandler->moveToThread(clientOutputHandlerThread); + TQTimer::singleShot(0, clientOutputHandler, SLOT(run())); + clientOutputHandlerThread->start(); while (1) { fd_set rfds, wfds, efds; @@ -587,7 +613,11 @@ clientInput(void *data) LOCK(cl->updateMutex); TSIGNAL(cl->updateCond); UNLOCK(cl->updateMutex); - IF_PTHREADS(pthread_join(output_thread, NULL)); + clientOutputHandlerThread->wait(); + delete clientOutputHandlerThread; + clientOutputHandlerThread = NULL; + delete clientOutputHandler; + clientOutputHandler = NULL; rfbClientConnectionGone(cl); @@ -604,6 +634,15 @@ listenerRun(void *data) socklen_t len; fd_set listen_fds; /* temp file descriptor list for select() */ + if (screen->inetdSock != -1) { + cl = rfbNewClient(screen, screen->inetdSock); + if (cl && !cl->onHold) + rfbStartOnHoldClient(cl); + else if (screen->inetdDisconnectHook && !cl) + screen->inetdDisconnectHook(); + return NULL; + } + /* TODO: this thread won't die by restarting the server */ /* TODO: HTTP is not handled */ while (1) { @@ -634,7 +673,18 @@ listenerRun(void *data) return(NULL); } -#endif +void +rfbStartOnHoldClient(rfbClientPtr cl) +{ + mOnHoldClientHandlerThread = new TQEventLoopThread(); + mOnHoldClientHandler = new OnHoldClientHandlerObject(); + mOnHoldClientHandler->d = cl; + mOnHoldClientHandler->moveToThread(mOnHoldClientHandlerThread); + TQTimer::singleShot(0, mOnHoldClientHandler, SLOT(run())); + mOnHoldClientHandlerThread->start(); +} + +#else void rfbStartOnHoldClient(rfbClientPtr cl) @@ -653,6 +703,7 @@ rfbStartOnHoldClient(rfbClientPtr cl) #endif } +#endif void rfbRefuseOnHoldClient(rfbClientPtr cl) @@ -736,7 +787,7 @@ static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl) static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len) { int i; - char *passwd=rfbDecryptPasswdFromFile(cl->screen->authPasswdData); + char *passwd=rfbDecryptPasswdFromFile((char*)cl->screen->authPasswdData); if(!passwd) { rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData); @@ -844,7 +895,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, int width,int height,int bitsPerSample,int samplesPerPixel, int bytesPerPixel) { - rfbScreenInfoPtr screen=calloc(sizeof(rfbScreenInfo),1); + rfbScreenInfoPtr screen=(rfbScreenInfoPtr)calloc(sizeof(rfbScreenInfo),1); if (! logMutex_initialized) { INIT_MUTEX(logMutex); @@ -953,6 +1004,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->setTranslateFunction = rfbSetTranslateFunction; screen->newClientHook = rfbDefaultNewClientHook; screen->displayHook = NULL; + screen->inetdDisconnectHook = NULL; screen->displayFinishedHook = NULL; screen->getKeyboardLedStateHook = NULL; screen->xvpHook = NULL; @@ -1049,7 +1101,22 @@ void rfbScreenCleanup(rfbScreenInfoPtr screen) cl1=cl; } rfbReleaseClientIterator(i); - + + if (mOnHoldClientHandlerThread) { + mOnHoldClientHandlerThread->exit(); + delete mOnHoldClientHandlerThread; + mOnHoldClientHandlerThread = NULL; + delete mOnHoldClientHandler; + mOnHoldClientHandler = NULL; + } + if (mControlPipeHandlerThread) { + mControlPipeHandlerThread->exit(); + delete mControlPipeHandlerThread; + mControlPipeHandlerThread = NULL; + delete mControlPipeHandler; + mControlPipeHandler = NULL; + } + #define FREE_IF(x) if(screen->x) free(screen->x) FREE_IF(colourMap.data.bytes); FREE_IF(underCursorBuffer); @@ -1239,12 +1306,15 @@ void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground { if(runInBackground) { #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - pthread_t listener_thread; - - screen->backgroundLoop = TRUE; - - pthread_create(&listener_thread, NULL, listenerRun, screen); - return; + screen->backgroundLoop = TRUE; + + mControlPipeHandlerThread = new TQEventLoopThread(); + mControlPipeHandler = new ControlPipeHandlerObject(); + mControlPipeHandler->d = screen; + mControlPipeHandler->moveToThread(mControlPipeHandlerThread); + TQTimer::singleShot(0, mControlPipeHandler, SLOT(run())); + mControlPipeHandlerThread->start(); + return; #else rfbErr("Can't run in background, because I don't have PThreads!\n"); return; @@ -1257,3 +1327,50 @@ void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground while(rfbIsActive(screen)) rfbProcessEvents(screen,usec); } + +ControlPipeHandlerObject::ControlPipeHandlerObject() : TQObject() { + // +} + +ControlPipeHandlerObject::~ControlPipeHandlerObject() { + // +} + +void ControlPipeHandlerObject::run(void) { + listenerRun(d); + + // Terminate thread + TQThread::exit(); +} + +OnHoldClientHandlerObject::OnHoldClientHandlerObject() : TQObject() { + // +} + +OnHoldClientHandlerObject::~OnHoldClientHandlerObject() { + // +} + +void OnHoldClientHandlerObject::run(void) { + clientInput(d); + + // Terminate thread + TQThread::exit(); +} + +ClientOutputHandlerObject::ClientOutputHandlerObject() : TQObject() { + // +} + +ClientOutputHandlerObject::~ClientOutputHandlerObject() { + // +} + +void ClientOutputHandlerObject::run(void) { + clientOutput(d); + + // Terminate thread + TQThread::exit(); +} + +#include "main.moc" diff --git a/libvncserver/main.cc b/libvncserver/main.cc new file mode 120000 index 0000000..8a03e94 --- /dev/null +++ b/libvncserver/main.cc @@ -0,0 +1 @@ +main.c
\ No newline at end of file diff --git a/libvncserver/main.h b/libvncserver/main.h new file mode 100644 index 0000000..a02a476 --- /dev/null +++ b/libvncserver/main.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>. + * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de> + * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. + * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * see GPL (latest version) for full details + */ + +#ifndef _MAIN_H +#define _MAIN_H + +class ControlPipeHandlerObject : public TQObject +{ + Q_OBJECT + + public: + ControlPipeHandlerObject(); + ~ControlPipeHandlerObject(); + + public slots: + void run(); + + public: + rfbScreenInfoPtr d; +}; + +class OnHoldClientHandlerObject : public TQObject +{ + Q_OBJECT + + public: + OnHoldClientHandlerObject(); + ~OnHoldClientHandlerObject(); + + public slots: + void run(); + + public: + rfbClientPtr d; +}; + +class ClientOutputHandlerObject : public TQObject +{ + Q_OBJECT + + public: + ClientOutputHandlerObject(); + ~ClientOutputHandlerObject(); + + public slots: + void run(); + + public: + rfbClientPtr d; +}; + +#endif // _MAIN_H + diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 7af8490..182d18b 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -3,6 +3,7 @@ */ /* + * Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>. * Copyright (C) 2011-2012 D. R. Commander * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin * Copyright (C) 2002 RealVNC Ltd. @@ -26,12 +27,6 @@ * USA. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#define _XOPEN_SOURCE 600 -#endif - #include <stdio.h> #include <string.h> #include <rfb/rfb.h> @@ -331,6 +326,7 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->clientData = NULL; cl->clientGoneHook = rfbDoNothingWithClient; + cl->negotiationFinishedHook = rfbDoNothingWithClient; if(isUDP) { rfbLog(" accepted UDP client\n"); @@ -435,6 +431,7 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->enableCursorPosUpdates = FALSE; cl->useRichCursorEncoding = FALSE; cl->enableLastRectEncoding = FALSE; + cl->disableBackground = FALSE; cl->enableKeyboardLedState = FALSE; cl->enableSupportedMessages = FALSE; cl->enableSupportedEncodings = FALSE; @@ -2105,6 +2102,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableCursorShapeUpdates = FALSE; cl->enableCursorShapeUpdates = FALSE; cl->enableLastRectEncoding = FALSE; + cl->disableBackground = FALSE; cl->enableKeyboardLedState = FALSE; cl->enableSupportedMessages = FALSE; cl->enableSupportedEncodings = FALSE; @@ -2193,6 +2191,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableLastRectEncoding = TRUE; } break; + case rfbEncodingBackground: + rfbLog("Disabling background for client " + "%s\n", cl->host); + cl->disableBackground = TRUE; + break; case rfbEncodingNewFBSize: if (!cl->useNewFBSize) { rfbLog("Enabling NewFBSize protocol extension for client " @@ -2349,6 +2352,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableCursorPosUpdates = FALSE; } + cl->negotiationFinishedHook(cl); + return; } diff --git a/libvncserver/scale.c b/libvncserver/scale.c index 2325dc3..7a63e86 100644 --- a/libvncserver/scale.c +++ b/libvncserver/scale.c @@ -25,9 +25,6 @@ * USA. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#endif #include <string.h> #include <rfb/rfb.h> #include <rfb/rfbregion.h> diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index fe54a37..4556197 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -42,15 +42,6 @@ * USA. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#ifdef __linux__ -/* Setting this on other systems hides definitions such as INADDR_LOOPBACK. - * The check should be for __GLIBC__ in fact. */ -# define _POSIX_SOURCE -#endif -#endif - #include <rfb/rfb.h> #ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 616c81c..4361bdd 100644 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -26,10 +26,6 @@ * USA. */ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#endif - #include <rfb/rfb.h> /* errno */ #include <errno.h> @@ -10,6 +10,7 @@ */ /* + * Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>. * Copyright (C) 2005 Rohit Kumar <rokumar@novell.com>, * Johannes E. Schindelin <johannes.schindelin@gmx.de> * Copyright (C) 2002 RealVNC Ltd. @@ -150,6 +151,7 @@ typedef rfbBool (*rfbSetTranslateFunctionProcPtr)(struct _rfbClientRec* cl); typedef rfbBool (*rfbPasswordCheckProcPtr)(struct _rfbClientRec* cl,const char* encryptedPassWord,int len); typedef enum rfbNewClientAction (*rfbNewClientHookPtr)(struct _rfbClientRec* cl); typedef void (*rfbDisplayHookPtr)(struct _rfbClientRec* cl); +typedef void (*rfbInetdDisconnectPtr)(); typedef void (*rfbDisplayFinishedHookPtr)(struct _rfbClientRec* cl, int result); /** support the capability to view the caps/num/scroll states of the X server */ typedef int (*rfbGetKeyboardLedStateHookPtr)(struct _rfbScreenInfo* screen); @@ -341,6 +343,9 @@ typedef struct _rfbScreenInfo rfbNewClientHookPtr newClientHook; /** displayHook is called just before a frame buffer update */ rfbDisplayHookPtr displayHook; + /* inetdDisconnectHook is called when the connection has been + interrupted before a client could connect. */ + rfbInetdDisconnectPtr inetdDisconnectHook; /** These hooks are called to pass keyboard state back to the client */ rfbGetKeyboardLedStateHookPtr getKeyboardLedStateHook; @@ -412,6 +417,7 @@ typedef struct sraRegion* sraRegionPtr; */ typedef void (*ClientGoneHookPtr)(struct _rfbClientRec* cl); +typedef void (*NegotiationFinishedHookPtr)(struct _rfbClientRec* cl); typedef struct _rfbFileTransferData { int fd; @@ -457,6 +463,9 @@ typedef struct _rfbClientRec { void* clientData; ClientGoneHookPtr clientGoneHook; + /* negotiationFinishedHook is called when the negotiation phase has ended */ + NegotiationFinishedHookPtr negotiationFinishedHook; + SOCKET sock; char *host; @@ -622,6 +631,7 @@ typedef struct _rfbClientRec { rfbBool enableServerIdentity; /**< client supports ServerIdentity encoding */ rfbBool enableKeyboardLedState; /**< client supports KeyboardState encoding */ rfbBool enableLastRectEncoding; /**< client supports LastRect encoding */ + rfbBool disableBackground; /**< client wants to disable background */ rfbBool enableCursorShapeUpdates; /**< client supports cursor shape updates */ rfbBool enableCursorPosUpdates; /**< client supports cursor position updates */ rfbBool useRichCursorEncoding; /**< rfbEncodingRichCursor is preferred */ diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h index 1a80f0d..2eea004 100644 --- a/rfb/rfbclient.h +++ b/rfb/rfbclient.h @@ -191,6 +191,8 @@ typedef void (*SoftCursorUnlockScreenProc)(struct _rfbClient* client); typedef void (*GotFrameBufferUpdateProc)(struct _rfbClient* client, int x, int y, int w, int h); typedef void (*FinishedFrameBufferUpdateProc)(struct _rfbClient* client); typedef char* (*GetPasswordProc)(struct _rfbClient* client); +typedef void (*AuthenticationResultsProc)(struct _rfbClient* client, uint32_t authResult); +typedef void (*NetworkStatusProc)(struct _rfbClient* client, uint32_t errorCode); typedef rfbCredential* (*GetCredentialProc)(struct _rfbClient* client, int credentialType); typedef rfbBool (*MallocFrameBufferProc)(struct _rfbClient* client); typedef void (*GotXCutTextProc)(struct _rfbClient* client, const char *text, int textlen); @@ -331,6 +333,8 @@ typedef struct _rfbClient { GotFrameBufferUpdateProc GotFrameBufferUpdate; /** the pointer returned by GetPassword will be freed after use! */ GetPasswordProc GetPassword; + AuthenticationResultsProc AuthenticationResults; + NetworkStatusProc NetworkStatus; MallocFrameBufferProc MallocFrameBuffer; GotXCutTextProc GotXCutText; BellProc Bell; diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index dfb2c28..8f7c2f9 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -13,6 +13,7 @@ */ /* + * Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>. All Rights Reserved. * Copyright (C) 2009-2010 D. R. Commander. All Rights Reserved. * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin * Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved. @@ -119,6 +120,14 @@ typedef uint32_t in_addr_t; #define MAX_ENCODINGS 64 +#define rfbNetworkConnectionSuccess 0 +#define rfbNetworkRFBConnectionSuccess 1 +#define rfbNetworkConnectionClosed 2 +#define rfbNetworkConnectionFailed 3 +#define rfbNetworkNameResolutionFailed 4 +#define rfbNetworkRFBServerNotValid 5 +#define rfbNetworkRFBProtocolFailure 6 + /***************************************************************************** * * Structures used in several messages @@ -444,6 +453,13 @@ typedef struct { #define rfbEncodingH264 0x48323634 +/* + * Reserved range for TDE: + * 0xFFFE0100 - 0xFFFE01FF + */ + +#define rfbEncodingBackground 0xFFFE0125 + /* Cache & XOR-Zlib - rdv@2002 */ #define rfbEncodingCache 0xFFFF0000 #define rfbEncodingCacheEnable 0xFFFF0001 diff --git a/test/copyrecttest.c b/test/copyrecttest.c index b3d3ada..d53c32d 100644 --- a/test/copyrecttest.c +++ b/test/copyrecttest.c @@ -1,6 +1,3 @@ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#endif #include <rfb/rfb.h> #define _USE_MATH_DEFINES #include <math.h> diff --git a/test/encodingstest.c b/test/encodingstest.c index 715e887..1d8e861 100644 --- a/test/encodingstest.c +++ b/test/encodingstest.c @@ -1,6 +1,3 @@ -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#endif #include <time.h> #include <stdarg.h> #include <rfb/rfb.h> |