Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 1 | /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. |
| 2 | * |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 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 | |
| 21 | extern "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 | |
| 55 | extern "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 | |
| 72 | using namespace rfb; |
| 73 | |
| 74 | static rfb::LogWriter vlog("vncext"); |
| 75 | |
| 76 | static unsigned long vncExtGeneration = 0; |
| 77 | static bool initialised = false; |
| 78 | static XserverDesktop* desktop[MAXSCREENS] = { 0, }; |
| 79 | void* vncFbptr[MAXSCREENS] = { 0, }; |
| 80 | |
| 81 | static char* clientCutText = 0; |
| 82 | static int clientCutTextLen = 0; |
| 83 | |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 84 | static XserverDesktop* queryConnectDesktop = 0; |
| 85 | static void* queryConnectId = 0; |
| 86 | static int queryConnectTimeout = 0; |
| 87 | static OsTimerPtr queryConnectTimer = 0; |
| 88 | |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 89 | static struct VncInputSelect* vncInputSelectHead = 0; |
| 90 | struct VncInputSelect { |
| 91 | VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m) |
| 92 | { |
| 93 | next = vncInputSelectHead; |
| 94 | vncInputSelectHead = this; |
| 95 | } |
| 96 | ClientPtr client; |
| 97 | Window window; |
| 98 | int mask; |
| 99 | VncInputSelect* next; |
| 100 | }; |
| 101 | |
| 102 | static int nPrevSelections = 0; |
| 103 | static TimeStamp* prevSelectionTimes = 0; |
| 104 | |
| 105 | static int vncErrorBase = 0; |
| 106 | static int vncEventBase = 0; |
| 107 | static char* vncPasswdFile = 0; |
| 108 | int vncInetdSock = -1; |
| 109 | |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 110 | rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile", |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 111 | &SSecurityFactoryStandard::vncAuthPasswdFile); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 112 | rfb::StringParameter httpDir("httpd", |
| 113 | "Directory containing files to serve via HTTP", |
| 114 | ""); |
| 115 | rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0); |
| 116 | rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis", |
| 117 | &rfb::Server::clientWaitTimeMillis); |
| 118 | rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0); |
| 119 | rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11"); |
| 120 | rfb::BoolParameter localhostOnly("localhost", |
| 121 | "Only allow connections from localhost", |
| 122 | false); |
| 123 | |
| 124 | void vncExtensionInit() |
| 125 | { |
| 126 | if (vncExtGeneration == serverGeneration) { |
| 127 | vlog.error("vncExtensionInit: called twice in same generation?"); |
| 128 | return; |
| 129 | } |
| 130 | vncExtGeneration = serverGeneration; |
| 131 | |
| 132 | ExtensionEntry* extEntry |
| 133 | = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors, |
| 134 | ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc, |
| 135 | StandardMinorOpcode); |
| 136 | if (!extEntry) { |
| 137 | ErrorF("vncExtInit: AddExtension failed\n"); |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | vncErrorBase = extEntry->errorBase; |
| 142 | vncEventBase = extEntry->eventBase; |
| 143 | |
| 144 | vlog.info("VNC extension running!"); |
| 145 | |
| 146 | if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) { |
| 147 | FatalError("AddCallback failed\n"); |
| 148 | } |
| 149 | |
| 150 | try { |
| 151 | if (!initialised) { |
| 152 | rfb::initStdIOLoggers(); |
| 153 | initialised = true; |
| 154 | } |
| 155 | |
| 156 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 157 | |
| 158 | if (!desktop[scr]) { |
| 159 | network::TcpListener* listener = 0; |
| 160 | network::TcpListener* httpListener = 0; |
| 161 | if (scr == 0 && vncInetdSock != -1) { |
| 162 | if (network::TcpSocket::isSocket(vncInetdSock) && |
| 163 | !network::TcpSocket::isConnected(vncInetdSock)) |
| 164 | { |
| 165 | listener = new network::TcpListener(0, 0, vncInetdSock, true); |
| 166 | vlog.info("inetd wait"); |
| 167 | } |
| 168 | } else { |
| 169 | int port = rfbport; |
| 170 | if (port == 0) port = 5900 + atoi(display); |
| 171 | port += 1000 * scr; |
| 172 | listener = new network::TcpListener(port, localhostOnly); |
| 173 | vlog.info("Listening for VNC connections on port %d",port); |
| 174 | CharArray httpDirStr(httpDir.getData()); |
| 175 | if (httpDirStr.buf[0]) { |
| 176 | port = httpPort; |
| 177 | if (port == 0) port = 5800 + atoi(display); |
| 178 | port += 1000 * scr; |
| 179 | httpListener = new network::TcpListener(port, localhostOnly); |
| 180 | vlog.info("Listening for HTTP connections on port %d",port); |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | CharArray desktopNameStr(desktopName.getData()); |
| 185 | desktop[scr] = new XserverDesktop(screenInfo.screens[scr], listener, |
| 186 | httpListener, |
| 187 | desktopNameStr.buf, |
| 188 | vncFbptr[scr]); |
| 189 | vlog.info("created VNC server for screen %d", scr); |
| 190 | |
| 191 | if (scr == 0 && vncInetdSock != -1 && !listener) { |
| 192 | network::Socket* sock = new network::TcpSocket(vncInetdSock); |
| 193 | desktop[scr]->addClient(sock, false); |
| 194 | vlog.info("added inetd sock"); |
| 195 | } |
| 196 | |
| 197 | } else { |
| 198 | desktop[scr]->serverReset(screenInfo.screens[scr]); |
| 199 | } |
| 200 | |
| 201 | vncHooksInit(screenInfo.screens[scr], desktop[scr]); |
| 202 | } |
| 203 | |
| 204 | RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0); |
| 205 | |
| 206 | } catch (rdr::Exception& e) { |
| 207 | vlog.error("vncExtInit: %s",e.str()); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | static void vncResetProc(ExtensionEntry* extEntry) |
| 212 | { |
| 213 | } |
| 214 | |
| 215 | // |
| 216 | // vncBlockHandler - called just before the X server goes into select(). Call |
| 217 | // on to the block handler for each desktop. Then check whether any of the |
| 218 | // selections have changed, and if so, notify any interested X clients. |
| 219 | // |
| 220 | |
| 221 | static void vncBlockHandler(pointer data, OSTimePtr timeout, pointer readmask) |
| 222 | { |
| 223 | fd_set* fds = (fd_set*)readmask; |
| 224 | |
| 225 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 226 | if (desktop[scr]) { |
| 227 | desktop[scr]->blockHandler(fds); |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | if (nPrevSelections != NumCurrentSelections) { |
| 232 | prevSelectionTimes |
| 233 | = (TimeStamp*)xnfrealloc(prevSelectionTimes, |
| 234 | NumCurrentSelections * sizeof(TimeStamp)); |
| 235 | for (int i = nPrevSelections; i < NumCurrentSelections; i++) { |
| 236 | prevSelectionTimes[i].months = 0; |
| 237 | prevSelectionTimes[i].milliseconds = 0; |
| 238 | } |
| 239 | nPrevSelections = NumCurrentSelections; |
| 240 | } |
| 241 | for (int i = 0; i < NumCurrentSelections; i++) { |
| 242 | if (CurrentSelections[i].lastTimeChanged.months |
| 243 | != prevSelectionTimes[i].months || |
| 244 | CurrentSelections[i].lastTimeChanged.milliseconds |
| 245 | != prevSelectionTimes[i].milliseconds) |
| 246 | { |
| 247 | SendSelectionChangeEvent(CurrentSelections[i].selection); |
| 248 | prevSelectionTimes[i] = CurrentSelections[i].lastTimeChanged; |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | static void vncWakeupHandler(pointer data, int nfds, pointer readmask) |
| 254 | { |
| 255 | fd_set* fds = (fd_set*)readmask; |
| 256 | |
| 257 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 258 | if (desktop[scr]) { |
| 259 | desktop[scr]->wakeupHandler(fds, nfds); |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | static void vncClientStateChange(CallbackListPtr*, pointer, pointer p) |
| 265 | { |
| 266 | ClientPtr client = ((NewClientInfoRec*)p)->client; |
| 267 | if (client->clientState == ClientStateGone) { |
| 268 | VncInputSelect** nextPtr = &vncInputSelectHead; |
| 269 | for (VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) { |
| 270 | if (cur->client == client) { |
| 271 | *nextPtr = cur->next; |
| 272 | delete cur; |
| 273 | continue; |
| 274 | } |
| 275 | nextPtr = &cur->next; |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | void vncBell() |
| 281 | { |
| 282 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 283 | if (desktop[scr]) { |
| 284 | desktop[scr]->bell(); |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | void vncClientGone(int fd) |
| 290 | { |
| 291 | if (fd == vncInetdSock) { |
| 292 | fprintf(stderr,"inetdSock client gone\n"); |
| 293 | GiveUp(0); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | void vncClientCutText(const char* str, int len) |
| 298 | { |
| 299 | delete [] clientCutText; |
| 300 | clientCutText = new char[len]; |
| 301 | memcpy(clientCutText, str, len); |
| 302 | clientCutTextLen = len; |
| 303 | xVncExtClientCutTextNotifyEvent ev; |
| 304 | ev.type = vncEventBase + VncExtClientCutTextNotify; |
| 305 | for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { |
| 306 | if (cur->mask & VncExtClientCutTextMask) { |
| 307 | ev.sequenceNumber = cur->client->sequence; |
| 308 | ev.window = cur->window; |
| 309 | ev.time = GetTimeInMillis(); |
| 310 | if (cur->client->swapped) { |
| 311 | int n; |
| 312 | swaps(&ev.sequenceNumber, n); |
| 313 | swapl(&ev.window, n); |
| 314 | swapl(&ev.time, n); |
| 315 | } |
| 316 | WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent), |
| 317 | (char *)&ev); |
| 318 | } |
| 319 | } |
| 320 | } |
| 321 | |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 322 | |
| 323 | static CARD32 queryConnectTimerCallback(OsTimerPtr timer, |
| 324 | CARD32 now, pointer arg) |
| 325 | { |
| 326 | if (queryConnectTimeout) |
| 327 | queryConnectDesktop->approveConnection(queryConnectId, false, "The attempt to prompt the user to accept the connection failed"); |
| 328 | // Re-notify clients, causing them to discover that we're done |
| 329 | vncQueryConnect(queryConnectDesktop, queryConnectId); |
| 330 | return 0; |
| 331 | } |
| 332 | |
| 333 | void vncQueryConnect(XserverDesktop* desktop, void* opaqueId) |
| 334 | { |
| 335 | // Only one query can be processed at any one time |
| 336 | if (queryConnectTimeout && ((desktop != queryConnectDesktop) || |
| 337 | (opaqueId != queryConnectId))) { |
| 338 | desktop->approveConnection(opaqueId, false, |
| 339 | "Another connection is currently being queried."); |
| 340 | return; |
| 341 | } |
| 342 | |
| 343 | // Get the query timeout. If it's zero, there is no query. |
| 344 | queryConnectTimeout = desktop->getQueryTimeout(opaqueId); |
| 345 | queryConnectId = queryConnectTimeout ? opaqueId : 0; |
| 346 | queryConnectDesktop = queryConnectTimeout ? desktop : 0; |
| 347 | |
| 348 | // Notify clients |
| 349 | bool notified = false; |
| 350 | xVncExtQueryConnectNotifyEvent ev; |
| 351 | ev.type = vncEventBase + VncExtQueryConnectNotify; |
| 352 | for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { |
| 353 | if (cur->mask & VncExtQueryConnectMask) { |
| 354 | ev.sequenceNumber = cur->client->sequence; |
| 355 | ev.window = cur->window; |
| 356 | if (cur->client->swapped) { |
| 357 | int n; |
| 358 | swaps(&ev.sequenceNumber, n); |
| 359 | swapl(&ev.window, n); |
| 360 | } |
| 361 | WriteToClient(cur->client, sizeof(xVncExtQueryConnectNotifyEvent), |
| 362 | (char *)&ev); |
| 363 | notified = true; |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | // If we're being asked to query a connection (rather than to cancel |
| 368 | // a query), and haven't been able to notify clients then reject it. |
| 369 | if (queryConnectTimeout && !notified) { |
| 370 | queryConnectTimeout = 0; |
| 371 | queryConnectId = 0; |
| 372 | queryConnectDesktop = 0; |
| 373 | desktop->approveConnection(opaqueId, false, |
| 374 | "Unable to query the local user to accept the connection."); |
| 375 | return; |
| 376 | } |
| 377 | |
| 378 | // Set a timer so that if no-one ever responds, we will eventually |
| 379 | // reject the connection |
| 380 | // NB: We don't set a timer if sock is null, since that indicates |
| 381 | // that pending queries should be cancelled. |
| 382 | if (queryConnectDesktop) |
| 383 | queryConnectTimer = TimerSet(queryConnectTimer, 0, |
| 384 | queryConnectTimeout*2000, |
| 385 | queryConnectTimerCallback, 0); |
| 386 | else |
| 387 | TimerCancel(queryConnectTimer); |
| 388 | } |
| 389 | |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 390 | static void SendSelectionChangeEvent(Atom selection) |
| 391 | { |
| 392 | xVncExtSelectionChangeNotifyEvent ev; |
| 393 | ev.type = vncEventBase + VncExtSelectionChangeNotify; |
| 394 | for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { |
| 395 | if (cur->mask & VncExtSelectionChangeMask) { |
| 396 | ev.sequenceNumber = cur->client->sequence; |
| 397 | ev.window = cur->window; |
| 398 | ev.selection = selection; |
| 399 | if (cur->client->swapped) { |
| 400 | int n; |
| 401 | swaps(&ev.sequenceNumber, n); |
| 402 | swapl(&ev.window, n); |
| 403 | swapl(&ev.selection, n); |
| 404 | } |
| 405 | WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent), |
| 406 | (char *)&ev); |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | static int ProcVncExtSetParam(ClientPtr client) |
| 412 | { |
| 413 | REQUEST(xVncExtSetParamReq); |
| 414 | REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen); |
| 415 | CharArray param(stuff->paramLen+1); |
| 416 | strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); |
| 417 | param.buf[stuff->paramLen] = 0; |
| 418 | |
| 419 | xVncExtSetParamReply rep; |
| 420 | int n; |
| 421 | rep.type = X_Reply; |
| 422 | rep.length = 0; |
| 423 | rep.sequenceNumber = client->sequence; |
| 424 | rep.success = rfb::Configuration::setParam(param.buf); |
| 425 | if (client->swapped) { |
| 426 | swaps(&rep.sequenceNumber, n); |
| 427 | swapl(&rep.length, n); |
| 428 | } |
| 429 | WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep); |
| 430 | return (client->noClientException); |
| 431 | } |
| 432 | |
| 433 | static int SProcVncExtSetParam(ClientPtr client) |
| 434 | { |
| 435 | register char n; |
| 436 | REQUEST(xVncExtSetParamReq); |
| 437 | swaps(&stuff->length, n); |
| 438 | REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq); |
| 439 | return ProcVncExtSetParam(client); |
| 440 | } |
| 441 | |
| 442 | static int ProcVncExtGetParam(ClientPtr client) |
| 443 | { |
| 444 | REQUEST(xVncExtGetParamReq); |
| 445 | REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen); |
| 446 | CharArray param(stuff->paramLen+1); |
| 447 | strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); |
| 448 | param.buf[stuff->paramLen] = 0; |
| 449 | |
| 450 | xVncExtGetParamReply rep; |
| 451 | int n; |
| 452 | rep.type = X_Reply; |
| 453 | rep.sequenceNumber = client->sequence; |
| 454 | rep.success = 0; |
| 455 | int len = 0; |
| 456 | char* value = 0; |
| 457 | rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf); |
| 458 | // Hack to avoid exposing password! |
| 459 | if (strcasecmp(param.buf, "Password") == 0) |
| 460 | p = 0; |
| 461 | if (p) { |
| 462 | value = p->getValueStr(); |
| 463 | rep.success = 1; |
| 464 | len = value ? strlen(value) : 0; |
| 465 | } |
| 466 | rep.length = (len + 3) >> 2; |
| 467 | rep.valueLen = len; |
| 468 | if (client->swapped) { |
| 469 | swaps(&rep.sequenceNumber, n); |
| 470 | swapl(&rep.length, n); |
| 471 | swaps(&rep.valueLen, n); |
| 472 | } |
| 473 | WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep); |
| 474 | if (value) |
| 475 | WriteToClient(client, len, value); |
| 476 | delete [] value; |
| 477 | return (client->noClientException); |
| 478 | } |
| 479 | |
| 480 | static int SProcVncExtGetParam(ClientPtr client) |
| 481 | { |
| 482 | register char n; |
| 483 | REQUEST(xVncExtGetParamReq); |
| 484 | swaps(&stuff->length, n); |
| 485 | REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq); |
| 486 | return ProcVncExtGetParam(client); |
| 487 | } |
| 488 | |
| 489 | static int ProcVncExtGetParamDesc(ClientPtr client) |
| 490 | { |
| 491 | REQUEST(xVncExtGetParamDescReq); |
| 492 | REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen); |
| 493 | CharArray param(stuff->paramLen+1); |
| 494 | strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); |
| 495 | param.buf[stuff->paramLen] = 0; |
| 496 | |
| 497 | xVncExtGetParamDescReply rep; |
| 498 | int n; |
| 499 | rep.type = X_Reply; |
| 500 | rep.sequenceNumber = client->sequence; |
| 501 | rep.success = 0; |
| 502 | int len = 0; |
| 503 | const char* desc = 0; |
| 504 | rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf); |
| 505 | if (p) { |
| 506 | desc = p->getDescription(); |
| 507 | rep.success = 1; |
| 508 | len = desc ? strlen(desc) : 0; |
| 509 | } |
| 510 | rep.length = (len + 3) >> 2; |
| 511 | rep.descLen = len; |
| 512 | if (client->swapped) { |
| 513 | swaps(&rep.sequenceNumber, n); |
| 514 | swapl(&rep.length, n); |
| 515 | swaps(&rep.descLen, n); |
| 516 | } |
| 517 | WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep); |
| 518 | if (desc) |
| 519 | WriteToClient(client, len, (char*)desc); |
| 520 | return (client->noClientException); |
| 521 | } |
| 522 | |
| 523 | static int SProcVncExtGetParamDesc(ClientPtr client) |
| 524 | { |
| 525 | register char n; |
| 526 | REQUEST(xVncExtGetParamDescReq); |
| 527 | swaps(&stuff->length, n); |
| 528 | REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq); |
| 529 | return ProcVncExtGetParamDesc(client); |
| 530 | } |
| 531 | |
| 532 | static int ProcVncExtListParams(ClientPtr client) |
| 533 | { |
| 534 | REQUEST(xVncExtListParamsReq); |
| 535 | REQUEST_SIZE_MATCH(xVncExtListParamsReq); |
| 536 | |
| 537 | xVncExtListParamsReply rep; |
| 538 | int n; |
| 539 | rep.type = X_Reply; |
| 540 | rep.sequenceNumber = client->sequence; |
| 541 | |
| 542 | int nParams = 0; |
| 543 | int len = 0; |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 544 | for (ParameterIterator i(Configuration::global()); i.param; i.next()) { |
| 545 | int l = strlen(i.param->getName()); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 546 | if (l <= 255) { |
| 547 | nParams++; |
| 548 | len += l + 1; |
| 549 | } |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 550 | } |
| 551 | rep.length = (len + 3) >> 2; |
| 552 | rep.nParams = nParams; |
| 553 | if (client->swapped) { |
| 554 | swaps(&rep.sequenceNumber, n); |
| 555 | swapl(&rep.length, n); |
| 556 | swaps(&rep.nParams, n); |
| 557 | } |
| 558 | WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep); |
| 559 | rdr::U8* data = new rdr::U8[len]; |
| 560 | rdr::U8* ptr = data; |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 561 | for (ParameterIterator i(Configuration::global()); i.param; i.next()) { |
| 562 | int l = strlen(i.param->getName()); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 563 | if (l <= 255) { |
| 564 | *ptr++ = l; |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 565 | memcpy(ptr, i.param->getName(), l); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 566 | ptr += l; |
| 567 | } |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 568 | } |
| 569 | WriteToClient(client, len, (char*)data); |
| 570 | delete [] data; |
| 571 | return (client->noClientException); |
| 572 | } |
| 573 | |
| 574 | static int SProcVncExtListParams(ClientPtr client) |
| 575 | { |
| 576 | register char n; |
| 577 | REQUEST(xVncExtListParamsReq); |
| 578 | swaps(&stuff->length, n); |
| 579 | REQUEST_SIZE_MATCH(xVncExtListParamsReq); |
| 580 | return ProcVncExtListParams(client); |
| 581 | } |
| 582 | |
| 583 | static int ProcVncExtSetServerCutText(ClientPtr client) |
| 584 | { |
| 585 | REQUEST(xVncExtSetServerCutTextReq); |
| 586 | REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen); |
| 587 | char* str = new char[stuff->textLen+1]; |
| 588 | strncpy(str, (char*)&stuff[1], stuff->textLen); |
| 589 | str[stuff->textLen] = 0; |
| 590 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 591 | if (desktop[scr]) { |
| 592 | desktop[scr]->serverCutText(str, stuff->textLen); |
| 593 | } |
| 594 | } |
| 595 | delete [] str; |
| 596 | return (client->noClientException); |
| 597 | } |
| 598 | |
| 599 | static int SProcVncExtSetServerCutText(ClientPtr client) |
| 600 | { |
| 601 | register char n; |
| 602 | REQUEST(xVncExtSetServerCutTextReq); |
| 603 | swaps(&stuff->length, n); |
| 604 | REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq); |
| 605 | swapl(&stuff->textLen, n); |
| 606 | return ProcVncExtSetServerCutText(client); |
| 607 | } |
| 608 | |
| 609 | static int ProcVncExtGetClientCutText(ClientPtr client) |
| 610 | { |
| 611 | REQUEST(xVncExtGetClientCutTextReq); |
| 612 | REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); |
| 613 | |
| 614 | xVncExtGetClientCutTextReply rep; |
| 615 | int n; |
| 616 | rep.type = X_Reply; |
| 617 | rep.length = (clientCutTextLen + 3) >> 2; |
| 618 | rep.sequenceNumber = client->sequence; |
| 619 | rep.textLen = clientCutTextLen; |
| 620 | if (client->swapped) { |
| 621 | swaps(&rep.sequenceNumber, n); |
| 622 | swapl(&rep.length, n); |
| 623 | swapl(&rep.textLen, n); |
| 624 | } |
| 625 | WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep); |
| 626 | if (clientCutText) |
| 627 | WriteToClient(client, clientCutTextLen, clientCutText); |
| 628 | return (client->noClientException); |
| 629 | } |
| 630 | |
| 631 | static int SProcVncExtGetClientCutText(ClientPtr client) |
| 632 | { |
| 633 | register char n; |
| 634 | REQUEST(xVncExtGetClientCutTextReq); |
| 635 | swaps(&stuff->length, n); |
| 636 | REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); |
| 637 | return ProcVncExtGetClientCutText(client); |
| 638 | } |
| 639 | |
| 640 | static int ProcVncExtSelectInput(ClientPtr client) |
| 641 | { |
| 642 | REQUEST(xVncExtSelectInputReq); |
| 643 | REQUEST_SIZE_MATCH(xVncExtSelectInputReq); |
| 644 | VncInputSelect** nextPtr = &vncInputSelectHead; |
| 645 | VncInputSelect* cur; |
| 646 | for (cur = vncInputSelectHead; cur; cur = *nextPtr) { |
| 647 | if (cur->client == client && cur->window == stuff->window) { |
| 648 | cur->mask = stuff->mask; |
| 649 | if (!cur->mask) { |
| 650 | *nextPtr = cur->next; |
| 651 | delete cur; |
| 652 | } |
| 653 | break; |
| 654 | } |
| 655 | nextPtr = &cur->next; |
| 656 | } |
| 657 | if (!cur) { |
| 658 | cur = new VncInputSelect(client, stuff->window, stuff->mask); |
| 659 | } |
| 660 | return (client->noClientException); |
| 661 | } |
| 662 | |
| 663 | static int SProcVncExtSelectInput(ClientPtr client) |
| 664 | { |
| 665 | register char n; |
| 666 | REQUEST(xVncExtSelectInputReq); |
| 667 | swaps(&stuff->length, n); |
| 668 | REQUEST_SIZE_MATCH(xVncExtSelectInputReq); |
| 669 | swapl(&stuff->window, n); |
| 670 | swapl(&stuff->mask, n); |
| 671 | return ProcVncExtSelectInput(client); |
| 672 | } |
| 673 | |
| 674 | static int ProcVncExtConnect(ClientPtr client) |
| 675 | { |
| 676 | REQUEST(xVncExtConnectReq); |
| 677 | REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen); |
| 678 | CharArray str(stuff->strLen+1); |
| 679 | strncpy(str.buf, (char*)&stuff[1], stuff->strLen); |
| 680 | str.buf[stuff->strLen] = 0; |
| 681 | |
| 682 | xVncExtConnectReply rep; |
| 683 | rep.success = 0; |
| 684 | if (desktop[0]) { |
| 685 | if (stuff->strLen == 0) { |
| 686 | try { |
| 687 | desktop[0]->disconnectClients(); |
| 688 | rep.success = 1; |
| 689 | } catch (rdr::Exception& e) { |
| 690 | vlog.error("Disconnecting all clients: %s",e.str()); |
| 691 | } |
| 692 | } else { |
| 693 | int port = 5500; |
| 694 | for (int i = 0; i < stuff->strLen; i++) { |
| 695 | if (str.buf[i] == ':') { |
| 696 | port = atoi(&str.buf[i+1]); |
| 697 | str.buf[i] = 0; |
| 698 | break; |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | try { |
| 703 | network::Socket* sock = new network::TcpSocket(str.buf, port); |
| 704 | desktop[0]->addClient(sock, true); |
| 705 | rep.success = 1; |
| 706 | } catch (rdr::Exception& e) { |
| 707 | vlog.error("Reverse connection: %s",e.str()); |
| 708 | } |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | rep.type = X_Reply; |
| 713 | rep.length = 0; |
| 714 | rep.sequenceNumber = client->sequence; |
| 715 | if (client->swapped) { |
| 716 | int n; |
| 717 | swaps(&rep.sequenceNumber, n); |
| 718 | swapl(&rep.length, n); |
| 719 | } |
| 720 | WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep); |
| 721 | return (client->noClientException); |
| 722 | } |
| 723 | |
| 724 | static int SProcVncExtConnect(ClientPtr client) |
| 725 | { |
| 726 | register char n; |
| 727 | REQUEST(xVncExtConnectReq); |
| 728 | swaps(&stuff->length, n); |
| 729 | REQUEST_AT_LEAST_SIZE(xVncExtConnectReq); |
| 730 | return ProcVncExtConnect(client); |
| 731 | } |
| 732 | |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 733 | |
| 734 | static int ProcVncExtGetQueryConnect(ClientPtr client) |
| 735 | { |
| 736 | REQUEST(xVncExtGetQueryConnectReq); |
| 737 | REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq); |
| 738 | |
| 739 | const char *qcAddress=0, *qcUsername=0; |
| 740 | int qcTimeout; |
| 741 | if (queryConnectDesktop) |
| 742 | qcTimeout = queryConnectDesktop->getQueryTimeout(queryConnectId, |
| 743 | &qcAddress, &qcUsername); |
| 744 | else |
| 745 | qcTimeout = 0; |
| 746 | |
| 747 | xVncExtGetQueryConnectReply rep; |
| 748 | int n; |
| 749 | rep.type = X_Reply; |
| 750 | rep.sequenceNumber = client->sequence; |
| 751 | rep.timeout = qcTimeout; |
| 752 | rep.addrLen = qcTimeout ? strlen(qcAddress) : 0; |
| 753 | rep.userLen = qcTimeout ? strlen(qcUsername) : 0; |
| 754 | rep.opaqueId = (CARD32)queryConnectId; |
| 755 | rep.length = (rep.userLen + rep.addrLen + 3) >> 2; |
| 756 | if (client->swapped) { |
| 757 | swaps(&rep.sequenceNumber, n); |
| 758 | swapl(&rep.userLen, n); |
| 759 | swapl(&rep.addrLen, n); |
| 760 | swapl(&rep.timeout, n); |
| 761 | swapl(&rep.opaqueId, n); |
| 762 | } |
| 763 | WriteToClient(client, sizeof(xVncExtGetQueryConnectReply), (char *)&rep); |
| 764 | if (qcTimeout) |
| 765 | WriteToClient(client, strlen(qcAddress), (char*)qcAddress); |
| 766 | if (qcTimeout) |
| 767 | WriteToClient(client, strlen(qcUsername), (char*)qcUsername); |
| 768 | return (client->noClientException); |
| 769 | } |
| 770 | |
| 771 | static int SProcVncExtGetQueryConnect(ClientPtr client) |
| 772 | { |
| 773 | register char n; |
| 774 | REQUEST(xVncExtGetQueryConnectReq); |
| 775 | swaps(&stuff->length, n); |
| 776 | REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq); |
| 777 | return ProcVncExtGetQueryConnect(client); |
| 778 | } |
| 779 | |
| 780 | |
| 781 | static int ProcVncExtApproveConnect(ClientPtr client) |
| 782 | { |
| 783 | REQUEST(xVncExtApproveConnectReq); |
| 784 | REQUEST_SIZE_MATCH(xVncExtApproveConnectReq); |
| 785 | if (queryConnectId == (void*)stuff->opaqueId) { |
| 786 | for (int scr = 0; scr < screenInfo.numScreens; scr++) { |
| 787 | if (desktop[scr]) { |
| 788 | desktop[scr]->approveConnection(queryConnectId, stuff->approve, |
| 789 | "Connection rejected by local user"); |
| 790 | } |
| 791 | } |
| 792 | // Inform other clients of the event and tidy up |
| 793 | vncQueryConnect(queryConnectDesktop, queryConnectId); |
| 794 | } |
| 795 | return (client->noClientException); |
| 796 | } |
| 797 | |
| 798 | static int SProcVncExtApproveConnect(ClientPtr client) |
| 799 | { |
| 800 | register char n; |
| 801 | REQUEST(xVncExtApproveConnectReq); |
| 802 | swaps(&stuff->length, n); |
| 803 | swapl(&stuff->opaqueId, n); |
| 804 | REQUEST_SIZE_MATCH(xVncExtApproveConnectReq); |
| 805 | return ProcVncExtApproveConnect(client); |
| 806 | } |
| 807 | |
| 808 | |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 809 | static int ProcVncExtDispatch(ClientPtr client) |
| 810 | { |
| 811 | REQUEST(xReq); |
| 812 | switch (stuff->data) { |
| 813 | case X_VncExtSetParam: |
| 814 | return ProcVncExtSetParam(client); |
| 815 | case X_VncExtGetParam: |
| 816 | return ProcVncExtGetParam(client); |
| 817 | case X_VncExtGetParamDesc: |
| 818 | return ProcVncExtGetParamDesc(client); |
| 819 | case X_VncExtListParams: |
| 820 | return ProcVncExtListParams(client); |
| 821 | case X_VncExtSetServerCutText: |
| 822 | return ProcVncExtSetServerCutText(client); |
| 823 | case X_VncExtGetClientCutText: |
| 824 | return ProcVncExtGetClientCutText(client); |
| 825 | case X_VncExtSelectInput: |
| 826 | return ProcVncExtSelectInput(client); |
| 827 | case X_VncExtConnect: |
| 828 | return ProcVncExtConnect(client); |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 829 | case X_VncExtGetQueryConnect: |
| 830 | return ProcVncExtGetQueryConnect(client); |
| 831 | case X_VncExtApproveConnect: |
| 832 | return ProcVncExtApproveConnect(client); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 833 | default: |
| 834 | return BadRequest; |
| 835 | } |
| 836 | } |
| 837 | |
| 838 | static int SProcVncExtDispatch(ClientPtr client) |
| 839 | { |
| 840 | REQUEST(xReq); |
| 841 | switch (stuff->data) { |
| 842 | case X_VncExtSetParam: |
| 843 | return SProcVncExtSetParam(client); |
| 844 | case X_VncExtGetParam: |
| 845 | return SProcVncExtGetParam(client); |
| 846 | case X_VncExtGetParamDesc: |
| 847 | return SProcVncExtGetParamDesc(client); |
| 848 | case X_VncExtListParams: |
| 849 | return SProcVncExtListParams(client); |
| 850 | case X_VncExtSetServerCutText: |
| 851 | return SProcVncExtSetServerCutText(client); |
| 852 | case X_VncExtGetClientCutText: |
| 853 | return SProcVncExtGetClientCutText(client); |
| 854 | case X_VncExtSelectInput: |
| 855 | return SProcVncExtSelectInput(client); |
| 856 | case X_VncExtConnect: |
| 857 | return SProcVncExtConnect(client); |
Constantin Kaplinsky | 95f6f7a | 2006-04-17 04:17:23 +0000 | [diff] [blame^] | 858 | case X_VncExtGetQueryConnect: |
| 859 | return SProcVncExtGetQueryConnect(client); |
| 860 | case X_VncExtApproveConnect: |
| 861 | return SProcVncExtApproveConnect(client); |
Constantin Kaplinsky | 47ed8d3 | 2004-10-08 09:43:57 +0000 | [diff] [blame] | 862 | default: |
| 863 | return BadRequest; |
| 864 | } |
| 865 | } |
| 866 | |