blob: ccaf5b8588ee227b00da2798304bdd4a149e3cef [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <stdio.h>
20
21extern "C" {
22#define class c_class
23#define NEED_EVENTS
24#include "X.h"
25#include "Xproto.h"
26#include "misc.h"
27#include "os.h"
28#include "dixstruct.h"
29#include "extnsionst.h"
30#include "scrnintstr.h"
31#include "selection.h"
32#define _VNCEXT_SERVER_
33#define _VNCEXT_PROTO_
34#include "vncExt.h"
35#undef class
36#undef xalloc
37}
38
39#include <rfb/Configuration.h>
40#include <rfb/Logger_stdio.h>
41#include <rfb/LogWriter.h>
42#include <rfb/util.h>
43#include <rfb/ServerCore.h>
44#include <rfb/SSecurityFactoryStandard.h>
45#include <rdr/HexOutStream.h>
46#include <rfb/LogWriter.h>
47#undef max
48#undef min
49#include <network/TcpSocket.h>
50
51#include "XserverDesktop.h"
52#include "vncHooks.h"
53#include "vncExtInit.h"
54
55extern "C" {
56
57 extern void vncExtensionInit();
58 static void vncResetProc(ExtensionEntry* extEntry);
59 static void vncBlockHandler(pointer data, OSTimePtr t, pointer readmask);
60 static void vncWakeupHandler(pointer data, int nfds, pointer readmask);
61 static void vncClientStateChange(CallbackListPtr*, pointer, pointer);
62 static void SendSelectionChangeEvent(Atom selection);
63 static int ProcVncExtDispatch(ClientPtr client);
64 static int SProcVncExtDispatch(ClientPtr client);
65
66 extern char *display;
67
68 extern Selection *CurrentSelections;
69 extern int NumCurrentSelections;
70}
71
72using namespace rfb;
73
74static rfb::LogWriter vlog("vncext");
75
76static unsigned long vncExtGeneration = 0;
77static bool initialised = false;
78static XserverDesktop* desktop[MAXSCREENS] = { 0, };
79void* vncFbptr[MAXSCREENS] = { 0, };
80
81static char* clientCutText = 0;
82static int clientCutTextLen = 0;
83
84static struct VncInputSelect* vncInputSelectHead = 0;
85struct VncInputSelect {
86 VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m)
87 {
88 next = vncInputSelectHead;
89 vncInputSelectHead = this;
90 }
91 ClientPtr client;
92 Window window;
93 int mask;
94 VncInputSelect* next;
95};
96
97static int nPrevSelections = 0;
98static TimeStamp* prevSelectionTimes = 0;
99
100static int vncErrorBase = 0;
101static int vncEventBase = 0;
102static char* vncPasswdFile = 0;
103int vncInetdSock = -1;
104
105rfb::VncAuthPasswdFileParameter vncAuthPasswdFile;
106rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile",
107 &vncAuthPasswdFile.param);
108rfb::StringParameter httpDir("httpd",
109 "Directory containing files to serve via HTTP",
110 "");
111rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0);
112rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
113 &rfb::Server::clientWaitTimeMillis);
114rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
115rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
116rfb::BoolParameter localhostOnly("localhost",
117 "Only allow connections from localhost",
118 false);
119
120void vncExtensionInit()
121{
122 if (vncExtGeneration == serverGeneration) {
123 vlog.error("vncExtensionInit: called twice in same generation?");
124 return;
125 }
126 vncExtGeneration = serverGeneration;
127
128 ExtensionEntry* extEntry
129 = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors,
130 ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc,
131 StandardMinorOpcode);
132 if (!extEntry) {
133 ErrorF("vncExtInit: AddExtension failed\n");
134 return;
135 }
136
137 vncErrorBase = extEntry->errorBase;
138 vncEventBase = extEntry->eventBase;
139
140 vlog.info("VNC extension running!");
141
142 if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) {
143 FatalError("AddCallback failed\n");
144 }
145
146 try {
147 if (!initialised) {
148 rfb::initStdIOLoggers();
149 initialised = true;
150 }
151
152 for (int scr = 0; scr < screenInfo.numScreens; scr++) {
153
154 if (!desktop[scr]) {
155 network::TcpListener* listener = 0;
156 network::TcpListener* httpListener = 0;
157 if (scr == 0 && vncInetdSock != -1) {
158 if (network::TcpSocket::isSocket(vncInetdSock) &&
159 !network::TcpSocket::isConnected(vncInetdSock))
160 {
161 listener = new network::TcpListener(0, 0, vncInetdSock, true);
162 vlog.info("inetd wait");
163 }
164 } else {
165 int port = rfbport;
166 if (port == 0) port = 5900 + atoi(display);
167 port += 1000 * scr;
168 listener = new network::TcpListener(port, localhostOnly);
169 vlog.info("Listening for VNC connections on port %d",port);
170 CharArray httpDirStr(httpDir.getData());
171 if (httpDirStr.buf[0]) {
172 port = httpPort;
173 if (port == 0) port = 5800 + atoi(display);
174 port += 1000 * scr;
175 httpListener = new network::TcpListener(port, localhostOnly);
176 vlog.info("Listening for HTTP connections on port %d",port);
177 }
178 }
179
180 CharArray desktopNameStr(desktopName.getData());
181 desktop[scr] = new XserverDesktop(screenInfo.screens[scr], listener,
182 httpListener,
183 desktopNameStr.buf,
184 vncFbptr[scr]);
185 vlog.info("created VNC server for screen %d", scr);
186
187 if (scr == 0 && vncInetdSock != -1 && !listener) {
188 network::Socket* sock = new network::TcpSocket(vncInetdSock);
189 desktop[scr]->addClient(sock, false);
190 vlog.info("added inetd sock");
191 }
192
193 } else {
194 desktop[scr]->serverReset(screenInfo.screens[scr]);
195 }
196
197 vncHooksInit(screenInfo.screens[scr], desktop[scr]);
198 }
199
200 RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0);
201
202 } catch (rdr::Exception& e) {
203 vlog.error("vncExtInit: %s",e.str());
204 }
205}
206
207static void vncResetProc(ExtensionEntry* extEntry)
208{
209}
210
211//
212// vncBlockHandler - called just before the X server goes into select(). Call
213// on to the block handler for each desktop. Then check whether any of the
214// selections have changed, and if so, notify any interested X clients.
215//
216
217static void vncBlockHandler(pointer data, OSTimePtr timeout, pointer readmask)
218{
219 fd_set* fds = (fd_set*)readmask;
220
221 for (int scr = 0; scr < screenInfo.numScreens; scr++) {
222 if (desktop[scr]) {
223 desktop[scr]->blockHandler(fds);
224 }
225 }
226
227 if (nPrevSelections != NumCurrentSelections) {
228 prevSelectionTimes
229 = (TimeStamp*)xnfrealloc(prevSelectionTimes,
230 NumCurrentSelections * sizeof(TimeStamp));
231 for (int i = nPrevSelections; i < NumCurrentSelections; i++) {
232 prevSelectionTimes[i].months = 0;
233 prevSelectionTimes[i].milliseconds = 0;
234 }
235 nPrevSelections = NumCurrentSelections;
236 }
237 for (int i = 0; i < NumCurrentSelections; i++) {
238 if (CurrentSelections[i].lastTimeChanged.months
239 != prevSelectionTimes[i].months ||
240 CurrentSelections[i].lastTimeChanged.milliseconds
241 != prevSelectionTimes[i].milliseconds)
242 {
243 SendSelectionChangeEvent(CurrentSelections[i].selection);
244 prevSelectionTimes[i] = CurrentSelections[i].lastTimeChanged;
245 }
246 }
247}
248
249static void vncWakeupHandler(pointer data, int nfds, pointer readmask)
250{
251 fd_set* fds = (fd_set*)readmask;
252
253 for (int scr = 0; scr < screenInfo.numScreens; scr++) {
254 if (desktop[scr]) {
255 desktop[scr]->wakeupHandler(fds, nfds);
256 }
257 }
258}
259
260static void vncClientStateChange(CallbackListPtr*, pointer, pointer p)
261{
262 ClientPtr client = ((NewClientInfoRec*)p)->client;
263 if (client->clientState == ClientStateGone) {
264 VncInputSelect** nextPtr = &vncInputSelectHead;
265 for (VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) {
266 if (cur->client == client) {
267 *nextPtr = cur->next;
268 delete cur;
269 continue;
270 }
271 nextPtr = &cur->next;
272 }
273 }
274}
275
276void vncBell()
277{
278 for (int scr = 0; scr < screenInfo.numScreens; scr++) {
279 if (desktop[scr]) {
280 desktop[scr]->bell();
281 }
282 }
283}
284
285void vncClientGone(int fd)
286{
287 if (fd == vncInetdSock) {
288 fprintf(stderr,"inetdSock client gone\n");
289 GiveUp(0);
290 }
291}
292
293void vncClientCutText(const char* str, int len)
294{
295 delete [] clientCutText;
296 clientCutText = new char[len];
297 memcpy(clientCutText, str, len);
298 clientCutTextLen = len;
299 xVncExtClientCutTextNotifyEvent ev;
300 ev.type = vncEventBase + VncExtClientCutTextNotify;
301 for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
302 if (cur->mask & VncExtClientCutTextMask) {
303 ev.sequenceNumber = cur->client->sequence;
304 ev.window = cur->window;
305 ev.time = GetTimeInMillis();
306 if (cur->client->swapped) {
307 int n;
308 swaps(&ev.sequenceNumber, n);
309 swapl(&ev.window, n);
310 swapl(&ev.time, n);
311 }
312 WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent),
313 (char *)&ev);
314 }
315 }
316}
317
318static void SendSelectionChangeEvent(Atom selection)
319{
320 xVncExtSelectionChangeNotifyEvent ev;
321 ev.type = vncEventBase + VncExtSelectionChangeNotify;
322 for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
323 if (cur->mask & VncExtSelectionChangeMask) {
324 ev.sequenceNumber = cur->client->sequence;
325 ev.window = cur->window;
326 ev.selection = selection;
327 if (cur->client->swapped) {
328 int n;
329 swaps(&ev.sequenceNumber, n);
330 swapl(&ev.window, n);
331 swapl(&ev.selection, n);
332 }
333 WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent),
334 (char *)&ev);
335 }
336 }
337}
338
339static int ProcVncExtSetParam(ClientPtr client)
340{
341 REQUEST(xVncExtSetParamReq);
342 REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen);
343 CharArray param(stuff->paramLen+1);
344 strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
345 param.buf[stuff->paramLen] = 0;
346
347 xVncExtSetParamReply rep;
348 int n;
349 rep.type = X_Reply;
350 rep.length = 0;
351 rep.sequenceNumber = client->sequence;
352 rep.success = rfb::Configuration::setParam(param.buf);
353 if (client->swapped) {
354 swaps(&rep.sequenceNumber, n);
355 swapl(&rep.length, n);
356 }
357 WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep);
358 return (client->noClientException);
359}
360
361static int SProcVncExtSetParam(ClientPtr client)
362{
363 register char n;
364 REQUEST(xVncExtSetParamReq);
365 swaps(&stuff->length, n);
366 REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq);
367 return ProcVncExtSetParam(client);
368}
369
370static int ProcVncExtGetParam(ClientPtr client)
371{
372 REQUEST(xVncExtGetParamReq);
373 REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen);
374 CharArray param(stuff->paramLen+1);
375 strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
376 param.buf[stuff->paramLen] = 0;
377
378 xVncExtGetParamReply rep;
379 int n;
380 rep.type = X_Reply;
381 rep.sequenceNumber = client->sequence;
382 rep.success = 0;
383 int len = 0;
384 char* value = 0;
385 rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
386 // Hack to avoid exposing password!
387 if (strcasecmp(param.buf, "Password") == 0)
388 p = 0;
389 if (p) {
390 value = p->getValueStr();
391 rep.success = 1;
392 len = value ? strlen(value) : 0;
393 }
394 rep.length = (len + 3) >> 2;
395 rep.valueLen = len;
396 if (client->swapped) {
397 swaps(&rep.sequenceNumber, n);
398 swapl(&rep.length, n);
399 swaps(&rep.valueLen, n);
400 }
401 WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep);
402 if (value)
403 WriteToClient(client, len, value);
404 delete [] value;
405 return (client->noClientException);
406}
407
408static int SProcVncExtGetParam(ClientPtr client)
409{
410 register char n;
411 REQUEST(xVncExtGetParamReq);
412 swaps(&stuff->length, n);
413 REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq);
414 return ProcVncExtGetParam(client);
415}
416
417static int ProcVncExtGetParamDesc(ClientPtr client)
418{
419 REQUEST(xVncExtGetParamDescReq);
420 REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen);
421 CharArray param(stuff->paramLen+1);
422 strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
423 param.buf[stuff->paramLen] = 0;
424
425 xVncExtGetParamDescReply rep;
426 int n;
427 rep.type = X_Reply;
428 rep.sequenceNumber = client->sequence;
429 rep.success = 0;
430 int len = 0;
431 const char* desc = 0;
432 rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
433 if (p) {
434 desc = p->getDescription();
435 rep.success = 1;
436 len = desc ? strlen(desc) : 0;
437 }
438 rep.length = (len + 3) >> 2;
439 rep.descLen = len;
440 if (client->swapped) {
441 swaps(&rep.sequenceNumber, n);
442 swapl(&rep.length, n);
443 swaps(&rep.descLen, n);
444 }
445 WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep);
446 if (desc)
447 WriteToClient(client, len, (char*)desc);
448 return (client->noClientException);
449}
450
451static int SProcVncExtGetParamDesc(ClientPtr client)
452{
453 register char n;
454 REQUEST(xVncExtGetParamDescReq);
455 swaps(&stuff->length, n);
456 REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq);
457 return ProcVncExtGetParamDesc(client);
458}
459
460static int ProcVncExtListParams(ClientPtr client)
461{
462 REQUEST(xVncExtListParamsReq);
463 REQUEST_SIZE_MATCH(xVncExtListParamsReq);
464
465 xVncExtListParamsReply rep;
466 int n;
467 rep.type = X_Reply;
468 rep.sequenceNumber = client->sequence;
469
470 int nParams = 0;
471 int len = 0;
472 rfb::VoidParameter* current = rfb::Configuration::head;
473 while (current) {
474 int l = strlen(current->getName());
475 if (l <= 255) {
476 nParams++;
477 len += l + 1;
478 }
479 current = current->_next;
480 }
481 rep.length = (len + 3) >> 2;
482 rep.nParams = nParams;
483 if (client->swapped) {
484 swaps(&rep.sequenceNumber, n);
485 swapl(&rep.length, n);
486 swaps(&rep.nParams, n);
487 }
488 WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep);
489 rdr::U8* data = new rdr::U8[len];
490 rdr::U8* ptr = data;
491 current = rfb::Configuration::head;
492 while (current) {
493 int l = strlen(current->getName());
494 if (l <= 255) {
495 *ptr++ = l;
496 memcpy(ptr, current->getName(), l);
497 ptr += l;
498 }
499 current = current->_next;
500 }
501 WriteToClient(client, len, (char*)data);
502 delete [] data;
503 return (client->noClientException);
504}
505
506static int SProcVncExtListParams(ClientPtr client)
507{
508 register char n;
509 REQUEST(xVncExtListParamsReq);
510 swaps(&stuff->length, n);
511 REQUEST_SIZE_MATCH(xVncExtListParamsReq);
512 return ProcVncExtListParams(client);
513}
514
515static int ProcVncExtSetServerCutText(ClientPtr client)
516{
517 REQUEST(xVncExtSetServerCutTextReq);
518 REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen);
519 char* str = new char[stuff->textLen+1];
520 strncpy(str, (char*)&stuff[1], stuff->textLen);
521 str[stuff->textLen] = 0;
522 for (int scr = 0; scr < screenInfo.numScreens; scr++) {
523 if (desktop[scr]) {
524 desktop[scr]->serverCutText(str, stuff->textLen);
525 }
526 }
527 delete [] str;
528 return (client->noClientException);
529}
530
531static int SProcVncExtSetServerCutText(ClientPtr client)
532{
533 register char n;
534 REQUEST(xVncExtSetServerCutTextReq);
535 swaps(&stuff->length, n);
536 REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq);
537 swapl(&stuff->textLen, n);
538 return ProcVncExtSetServerCutText(client);
539}
540
541static int ProcVncExtGetClientCutText(ClientPtr client)
542{
543 REQUEST(xVncExtGetClientCutTextReq);
544 REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
545
546 xVncExtGetClientCutTextReply rep;
547 int n;
548 rep.type = X_Reply;
549 rep.length = (clientCutTextLen + 3) >> 2;
550 rep.sequenceNumber = client->sequence;
551 rep.textLen = clientCutTextLen;
552 if (client->swapped) {
553 swaps(&rep.sequenceNumber, n);
554 swapl(&rep.length, n);
555 swapl(&rep.textLen, n);
556 }
557 WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep);
558 if (clientCutText)
559 WriteToClient(client, clientCutTextLen, clientCutText);
560 return (client->noClientException);
561}
562
563static int SProcVncExtGetClientCutText(ClientPtr client)
564{
565 register char n;
566 REQUEST(xVncExtGetClientCutTextReq);
567 swaps(&stuff->length, n);
568 REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
569 return ProcVncExtGetClientCutText(client);
570}
571
572static int ProcVncExtSelectInput(ClientPtr client)
573{
574 REQUEST(xVncExtSelectInputReq);
575 REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
576 VncInputSelect** nextPtr = &vncInputSelectHead;
577 VncInputSelect* cur;
578 for (cur = vncInputSelectHead; cur; cur = *nextPtr) {
579 if (cur->client == client && cur->window == stuff->window) {
580 cur->mask = stuff->mask;
581 if (!cur->mask) {
582 *nextPtr = cur->next;
583 delete cur;
584 }
585 break;
586 }
587 nextPtr = &cur->next;
588 }
589 if (!cur) {
590 cur = new VncInputSelect(client, stuff->window, stuff->mask);
591 }
592 return (client->noClientException);
593}
594
595static int SProcVncExtSelectInput(ClientPtr client)
596{
597 register char n;
598 REQUEST(xVncExtSelectInputReq);
599 swaps(&stuff->length, n);
600 REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
601 swapl(&stuff->window, n);
602 swapl(&stuff->mask, n);
603 return ProcVncExtSelectInput(client);
604}
605
606static int ProcVncExtConnect(ClientPtr client)
607{
608 REQUEST(xVncExtConnectReq);
609 REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen);
610 CharArray str(stuff->strLen+1);
611 strncpy(str.buf, (char*)&stuff[1], stuff->strLen);
612 str.buf[stuff->strLen] = 0;
613
614 xVncExtConnectReply rep;
615 rep.success = 0;
616 if (desktop[0]) {
617 if (stuff->strLen == 0) {
618 try {
619 desktop[0]->disconnectClients();
620 rep.success = 1;
621 } catch (rdr::Exception& e) {
622 vlog.error("Disconnecting all clients: %s",e.str());
623 }
624 } else {
625 int port = 5500;
626 for (int i = 0; i < stuff->strLen; i++) {
627 if (str.buf[i] == ':') {
628 port = atoi(&str.buf[i+1]);
629 str.buf[i] = 0;
630 break;
631 }
632 }
633
634 try {
635 network::Socket* sock = new network::TcpSocket(str.buf, port);
636 desktop[0]->addClient(sock, true);
637 rep.success = 1;
638 } catch (rdr::Exception& e) {
639 vlog.error("Reverse connection: %s",e.str());
640 }
641 }
642 }
643
644 rep.type = X_Reply;
645 rep.length = 0;
646 rep.sequenceNumber = client->sequence;
647 if (client->swapped) {
648 int n;
649 swaps(&rep.sequenceNumber, n);
650 swapl(&rep.length, n);
651 }
652 WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep);
653 return (client->noClientException);
654}
655
656static int SProcVncExtConnect(ClientPtr client)
657{
658 register char n;
659 REQUEST(xVncExtConnectReq);
660 swaps(&stuff->length, n);
661 REQUEST_AT_LEAST_SIZE(xVncExtConnectReq);
662 return ProcVncExtConnect(client);
663}
664
665static int ProcVncExtDispatch(ClientPtr client)
666{
667 REQUEST(xReq);
668 switch (stuff->data) {
669 case X_VncExtSetParam:
670 return ProcVncExtSetParam(client);
671 case X_VncExtGetParam:
672 return ProcVncExtGetParam(client);
673 case X_VncExtGetParamDesc:
674 return ProcVncExtGetParamDesc(client);
675 case X_VncExtListParams:
676 return ProcVncExtListParams(client);
677 case X_VncExtSetServerCutText:
678 return ProcVncExtSetServerCutText(client);
679 case X_VncExtGetClientCutText:
680 return ProcVncExtGetClientCutText(client);
681 case X_VncExtSelectInput:
682 return ProcVncExtSelectInput(client);
683 case X_VncExtConnect:
684 return ProcVncExtConnect(client);
685 default:
686 return BadRequest;
687 }
688}
689
690static int SProcVncExtDispatch(ClientPtr client)
691{
692 REQUEST(xReq);
693 switch (stuff->data) {
694 case X_VncExtSetParam:
695 return SProcVncExtSetParam(client);
696 case X_VncExtGetParam:
697 return SProcVncExtGetParam(client);
698 case X_VncExtGetParamDesc:
699 return SProcVncExtGetParamDesc(client);
700 case X_VncExtListParams:
701 return SProcVncExtListParams(client);
702 case X_VncExtSetServerCutText:
703 return SProcVncExtSetServerCutText(client);
704 case X_VncExtGetClientCutText:
705 return SProcVncExtGetClientCutText(client);
706 case X_VncExtSelectInput:
707 return SProcVncExtSelectInput(client);
708 case X_VncExtConnect:
709 return SProcVncExtConnect(client);
710 default:
711 return BadRequest;
712 }
713}
714