@@ -1446,6 +1446,13 @@ SetFormatAndEncodings(rfbClient* client)
14461446 if (se -> nEncodings < MAX_ENCODINGS )
14471447 encs [se -> nEncodings ++ ] = rfbClientSwap32IfLE (rfbEncodingQemuExtendedKeyEvent );
14481448
1449+ #ifdef LIBVNCSERVER_HAVE_LIBZ
1450+ /* extendedclipboard */
1451+ if (client -> clipboardCap )
1452+ if (se -> nEncodings < MAX_ENCODINGS )
1453+ encs [se -> nEncodings ++ ] = rfbClientSwap32IfLE (rfbEncodingExtendedClipboard );
1454+ #endif
1455+
14491456 /* client extensions */
14501457 for (e = rfbClientExtensions ; e ; e = e -> next )
14511458 if (e -> encodings ) {
@@ -1769,6 +1776,78 @@ SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBo
17691776}
17701777
17711778
1779+ #ifdef LIBVNCSERVER_HAVE_LIBZ
1780+ /*
1781+ * sendClientCutTextNotify
1782+ * it is needed when client send utf8 clipboard data
1783+ * please refer to
1784+ * https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#extended-clipboard-pseudo-encoding
1785+ * to supprot utf-8 clipboard we need at least support notify and text
1786+ */
1787+
1788+ static rfbBool
1789+ sendExtClientCutTextNotify (rfbClient * client )
1790+ {
1791+ rfbClientCutTextMsg cct = {0 , };
1792+ const uint32_t be_flags = rfbClientSwap32IfLE (rfbExtendedClipboard_Notify
1793+ | rfbExtendedClipboard_Text ); /*text and notify*/
1794+ cct .type = rfbClientCutText ;
1795+ cct .length = rfbClientSwap32IfLE (- ((uint32_t )sizeof (be_flags )));/*flag*/
1796+ rfbBool ret = WriteToRFBServer (client , (char * )& cct , sz_rfbClientCutTextMsg )
1797+ && WriteToRFBServer (client , (char * )& be_flags , sizeof (be_flags ));
1798+ return ret ;
1799+ }
1800+
1801+
1802+ /*
1803+ * sendClientCutTextProvide
1804+ * it need send notify first to grab clipboard (server will check that)
1805+ */
1806+
1807+ static rfbBool
1808+ sendExtClientCutTextProvide (rfbClient * client , char * data , int len )
1809+ {
1810+ rfbClientCutTextMsg cct = {0 , };
1811+ const uint32_t be_flags = rfbClientSwap32IfLE (rfbExtendedClipboard_Provide
1812+ | rfbExtendedClipboard_Text ); /*text and provide*/
1813+ const uint32_t be_size = rfbClientSwap32IfLE (len );
1814+ const size_t sz_to_compressed = sizeof (be_size ) + len ; /*size, data*/
1815+ size_t csz = compressBound (sz_to_compressed + 1 ); /*tricky, some server need extar byte to flush data*/
1816+
1817+ unsigned char * buf = malloc (sz_to_compressed + 1 ); /*tricky, some server need extra byte to flush data*/
1818+ if (!buf ) {
1819+ rfbClientLog ("sendExtClientCutTextProvide. alloc buf failed\n" );
1820+ return FALSE;
1821+ }
1822+ memcpy (buf , & be_size , sizeof (be_size ));
1823+ memcpy (buf + sizeof (be_size ), data , len );
1824+ buf [sz_to_compressed ] = 0 ;
1825+
1826+ unsigned char * cbuf = malloc (sizeof (be_flags ) + csz ); /*flag, compressed*/
1827+ if (!cbuf ) {
1828+ rfbClientLog ("sendExtClientCutTextProvide. alloc cbuf failed\n" );
1829+ free (buf );
1830+ return FALSE;
1831+ }
1832+ memcpy (cbuf , & be_flags , sizeof (be_flags ));
1833+ if (compress (cbuf + sizeof (be_flags ), & csz , buf , sz_to_compressed + 1 ) != Z_OK ) {
1834+ rfbClientLog ("sendExtClientCutTextProvide: compress cbuf failed\n" );
1835+ free (buf );
1836+ free (cbuf );
1837+ return FALSE;
1838+ }
1839+
1840+ cct .type = rfbClientCutText ;
1841+ cct .length = rfbClientSwap32IfLE (- (sizeof (be_flags ) + csz ));/*flag, compressed*/
1842+ rfbBool ret = sendExtClientCutTextNotify (client )
1843+ && WriteToRFBServer (client , (char * )& cct , sz_rfbClientCutTextMsg )
1844+ && WriteToRFBServer (client , (char * )cbuf , sizeof (be_flags ) + csz );
1845+ free (buf );
1846+ free (cbuf );
1847+ return ret ;
1848+ }
1849+ #endif
1850+
17721851/*
17731852 * SendClientCutText.
17741853 */
@@ -1780,6 +1859,12 @@ SendClientCutText(rfbClient* client, char *str, int len)
17801859
17811860 if (!SupportsClient2Server (client , rfbClientCutText )) return TRUE;
17821861
1862+ #ifdef LIBVNCSERVER_HAVE_LIBZ
1863+ if (client -> clipboardEnabledCap ) { /* if enabled extended clipboard, use it */
1864+ return sendExtClientCutTextProvide (client , str , len );
1865+ }
1866+ #endif
1867+
17831868 memset (& cct , 0 , sizeof (cct ));
17841869 cct .type = rfbClientCutText ;
17851870 cct .length = rfbClientSwap32IfLE (len );
@@ -1788,6 +1873,101 @@ SendClientCutText(rfbClient* client, char *str, int len)
17881873}
17891874
17901875
1876+ #ifdef LIBVNCSERVER_HAVE_LIBZ
1877+ /*
1878+ * process server clipboard extend text
1879+ */
1880+
1881+ static rfbBool
1882+ rfbClientProcessExtServerCutText (rfbClient * client , char * data , int len )
1883+ {
1884+ uint32_t flags ;
1885+ if (len < sizeof (flags )) {
1886+ rfbClientLog ("rfbClientProcessExtServerCutText. len < 4\n" );
1887+ return FALSE;
1888+ }
1889+ memcpy (& flags , data , sizeof (flags ));
1890+ data += sizeof (flags );
1891+ len -= sizeof (flags );
1892+ flags = rfbClientSwap32IfLE (flags );
1893+
1894+ /*
1895+ * only process (text | provide). Ignore all others
1896+ * modify here if need more types(rtf,html,dib,files)
1897+ */
1898+ if (!(flags & rfbExtendedClipboard_Text )) {
1899+ rfbClientLog ("rfbClientProcessExtServerCutText. not text type. ignore\n" );
1900+ return TRUE;
1901+ }
1902+ if (!(flags & rfbExtendedClipboard_Provide )) {
1903+ rfbClientLog ("rfbClientProcessExtServerCutText. not provide type. ignore\n" );
1904+ return TRUE;
1905+ }
1906+ if (flags & rfbExtendedClipboard_Caps ) {
1907+ rfbClientLog ("rfbClientProcessExtServerCutText. default cap.\n" );
1908+ client -> clipboardEnabledCap |= rfbExtendedClipboard_Text ; /* for now, only text */
1909+ return TRUE;
1910+ }
1911+
1912+ z_stream stream ;
1913+ stream .zalloc = NULL ;
1914+ stream .zfree = NULL ;
1915+ stream .opaque = NULL ;
1916+ stream .avail_in = 0 ;
1917+ stream .next_in = NULL ;
1918+ if (inflateInit (& stream ) != Z_OK ) {
1919+ rfbClientLog ("rfbClientProcessExtServerCutText. inflateInit failed\n" );
1920+ return FALSE;
1921+ }
1922+ stream .avail_in = len ;
1923+ stream .next_in = (unsigned char * )data ;
1924+
1925+ uint32_t size ;
1926+ stream .avail_out = sizeof (size );
1927+ stream .next_out = (unsigned char * )& size ;
1928+ if (inflate (& stream , Z_SYNC_FLUSH ) != Z_OK ) {
1929+ rfbClientLog ("rfbClientProcessExtServerCutText. inflate size failed\n" );
1930+ inflateEnd (& stream );
1931+ return FALSE;
1932+ }
1933+ size = rfbClientSwap32IfLE (size );
1934+ if (size > (1 << 20 )) {
1935+ rfbClientLog ("rfbClientProcessExtServerCutText. size too large\n" );
1936+ inflateEnd (& stream );
1937+ return FALSE;
1938+ }
1939+
1940+ unsigned char * buf = malloc (size );
1941+ if (!buf ) {
1942+ rfbClientLog ("rfbClientProcessExtServerCutText. alloc buf failed\n" );
1943+ inflateEnd (& stream );
1944+ return FALSE;
1945+ }
1946+ stream .avail_out = size ;
1947+ stream .next_out = buf ;
1948+ uLong out_before = stream .total_out ;
1949+ int err = inflate (& stream , Z_SYNC_FLUSH );
1950+ if (err != Z_OK && err != Z_STREAM_END ) {
1951+ rfbClientLog ("rfbClientProcessExtServerCutText. inflate buf failed\n" );
1952+ free (buf );
1953+ inflateEnd (& stream );
1954+ return FALSE;
1955+ }
1956+ if ((stream .total_out - out_before ) != size ) {
1957+ rfbClientLog ("rfbClientProcessExtServerCutText. inflate size error\n" );
1958+ free (buf );
1959+ inflateEnd (& stream );
1960+ return FALSE;
1961+ }
1962+ if (client -> GotXCutText )
1963+ client -> GotXCutText (client , (char * )buf , size );
1964+ free (buf );
1965+
1966+ inflateEnd (& stream );
1967+ return TRUE;
1968+ }
1969+ #endif
1970+
17911971
17921972/*
17931973 * HandleRFBServerMessage.
@@ -2332,12 +2512,31 @@ HandleRFBServerMessage(rfbClient* client)
23322512 case rfbServerCutText :
23332513 {
23342514 char * buffer ;
2515+ #ifdef LIBVNCSERVER_HAVE_LIBZ
2516+ int32_t ilen ; /* also as a flag, if ilen < 0, it is ext clipboard text */
2517+ rfbBool fallback = FALSE;
2518+ #endif
23352519
23362520 if (!ReadFromRFBServer (client , ((char * )& msg ) + 1 ,
23372521 sz_rfbServerCutTextMsg - 1 ))
23382522 return FALSE;
23392523
2524+ #ifdef LIBVNCSERVER_HAVE_LIBZ
2525+ ilen = rfbClientSwap32IfLE (msg .sct .length );
2526+ if (client -> clipboardEnabledCap && ilen >= 0 ) {
2527+ if (!client -> GotXCutTextFallback ) {
2528+ rfbClientLog ("extend clipboardCap enabled but msg len:%d >= 0. no fallback callback. ignore\n" , ilen );
2529+ return FALSE;
2530+ } else {
2531+ rfbClientLog ("extend clipboardCap enabled but msg len:%d >= 0. fallback\n" , ilen );
2532+ fallback = TRUE;
2533+ }
2534+ }
2535+
2536+ msg .sct .length = ilen < 0 ? - ilen : ilen ;
2537+ #else
23402538 msg .sct .length = rfbClientSwap32IfLE (msg .sct .length );
2539+ #endif
23412540
23422541 if (msg .sct .length > 1 <<20 ) {
23432542 rfbClientErr ("Ignoring too big cut text length sent by server: %u B > 1 MB\n" , (unsigned int )msg .sct .length );
@@ -2353,8 +2552,20 @@ HandleRFBServerMessage(rfbClient* client)
23532552
23542553 buffer [msg .sct .length ] = 0 ;
23552554
2555+ #ifdef LIBVNCSERVER_HAVE_LIBZ
2556+ if (fallback ) {
2557+ client -> GotXCutTextFallback (client , buffer , msg .sct .length );
2558+ } else if (ilen < 0 ) {
2559+ if (!rfbClientProcessExtServerCutText (client , buffer , - ilen )) {
2560+ free (buffer );
2561+ return FALSE;
2562+ }
2563+ } else if (client -> GotXCutText )
2564+ client -> GotXCutText (client , buffer , msg .sct .length );
2565+ #else
23562566 if (client -> GotXCutText )
23572567 client -> GotXCutText (client , buffer , msg .sct .length );
2568+ #endif
23582569
23592570 free (buffer );
23602571
0 commit comments