blob: 8b6adda6517bee2431415ea5113306d160d2dad1 [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 <rfb/VNCSConnectionST.h>
20#include <rfb/LogWriter.h>
21#include <rfb/secTypes.h>
22#include <rfb/ServerCore.h>
23#include <rfb/ComparingUpdateTracker.h>
24#define XK_MISCELLANY
25#define XK_XKB_KEYS
26#include <rfb/keysymdef.h>
27
28using namespace rfb;
29
30static LogWriter vlog("VNCSConnST");
31
32VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
33 bool reverse)
34 : sock(s), reverseConnection(reverse), server(server_),
35 image_getter(server->useEconomicTranslate),
36 drawRenderedCursor(false), removeRenderedCursor(false),
Oleg Sheikin4b0304f2005-12-09 10:59:12 +000037 pointerEventTime(0), accessRights(AccessDefault),
38 startTime(time(0))
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000039{
40 setStreams(&sock->inStream(), &sock->outStream());
41 peerEndpoint.buf = sock->getPeerEndpoint();
42 VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
43
44 setSocketTimeouts();
45 lastEventTime = time(0);
46
47 // Initialise security
48 CharArray sec_types_str;
49 if (reverseConnection)
50 sec_types_str.buf = rfb::Server::rev_sec_types.getData();
51 else
52 sec_types_str.buf = rfb::Server::sec_types.getData();
53 std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
54 std::list<int>::iterator i;
55 for (i=sec_types.begin(); i!=sec_types.end(); i++) {
56 addSecType(*i);
57 }
58
59 server->clients.push_front(this);
60}
61
62
63VNCSConnectionST::~VNCSConnectionST()
64{
65 // If we reach here then VNCServerST is deleting us!
66 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
67 peerEndpoint.buf, closeReason.buf);
68
69 // Release any keys the client still had pressed
70 std::set<rdr::U32>::iterator i;
71 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
72 server->desktop->keyEvent(*i, false);
73 if (server->pointerClient == this)
74 server->pointerClient = 0;
75
76 // Remove this client from the server
77 server->clients.remove(this);
78}
79
80
81// Methods called from VNCServerST
82
83bool VNCSConnectionST::init()
84{
85 try {
86 initialiseProtocol();
87 } catch (rdr::Exception& e) {
88 close(e.str());
89 return false;
90 }
91 return true;
92}
93
94void VNCSConnectionST::close(const char* reason)
95{
96 // Log the reason for the close
97 if (!closeReason.buf)
98 closeReason.buf = strDup(reason);
99 else
100 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
101
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000102 if (authenticated()) {
103 server->lastDisconnectTime = time(0);
104 }
105
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000106 // Just shutdown the socket. This will cause processMessages to
107 // eventually fail, causing us and our socket to be deleted.
108 sock->shutdown();
109 setState(RFBSTATE_CLOSING);
110}
111
112
113bool VNCSConnectionST::processMessages()
114{
115 if (state() == RFBSTATE_CLOSING) return false;
116 try {
117 // - Now set appropriate socket timeouts and process data
118 setSocketTimeouts();
119 bool clientsReadyBefore = server->clientsReadyForUpdate();
120
121 while (getInStream()->checkNoWait(1)) {
122 processMsg();
123 }
124
125 if (!clientsReadyBefore && !requested.is_empty())
126 server->desktop->framebufferUpdateRequest();
127
128 return true;
129
130 } catch (rdr::EndOfStream&) {
131 close("Clean disconnection");
132 } catch (rdr::Exception &e) {
133 close(e.str());
134 }
135 return false;
136}
137
138void VNCSConnectionST::writeFramebufferUpdateOrClose()
139{
140 try {
141 writeFramebufferUpdate();
142 } catch(rdr::Exception &e) {
143 close(e.str());
144 }
145}
146
147void VNCSConnectionST::pixelBufferChange()
148{
149 try {
150 if (!authenticated()) return;
151 if (cp.width && cp.height && (server->pb->width() != cp.width ||
152 server->pb->height() != cp.height))
153 {
154 // We need to clip the next update to the new size, but also add any
155 // extra bits if it's bigger. If we wanted to do this exactly, something
156 // like the code below would do it, but at the moment we just update the
157 // entire new size. However, we do need to clip the renderedCursorRect
158 // because that might be added to updates in writeFramebufferUpdate().
159
160 //updates.intersect(server->pb->getRect());
161 //
162 //if (server->pb->width() > cp.width)
163 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
164 // server->pb->height()));
165 //if (server->pb->height() > cp.height)
166 // updates.add_changed(Rect(0, cp.height, cp.width,
167 // server->pb->height()));
168
169 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
170
171 cp.width = server->pb->width();
172 cp.height = server->pb->height();
173 if (!writer()->writeSetDesktopSize()) {
174 close("Client does not support desktop resize");
175 return;
176 }
177 }
178 // Just update the whole screen at the moment because we're too lazy to
179 // work out what's actually changed.
180 updates.clear();
181 updates.add_changed(server->pb->getRect());
182 vlog.debug("pixel buffer changed - re-initialising image getter");
183 image_getter.init(server->pb, cp.pf(), writer());
184 if (writer()->needFakeUpdate())
185 writeFramebufferUpdate();
186 } catch(rdr::Exception &e) {
187 close(e.str());
188 }
189}
190
191void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
192{
193 try {
194 setColourMapEntries(firstColour, nColours);
195 } catch(rdr::Exception& e) {
196 close(e.str());
197 }
198}
199
200void VNCSConnectionST::bell()
201{
202 try {
203 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
204 } catch(rdr::Exception& e) {
205 close(e.str());
206 }
207}
208
209void VNCSConnectionST::serverCutText(const char *str, int len)
210{
211 try {
212 if (!(accessRights & AccessCutText)) return;
213 if (!rfb::Server::sendCutText) return;
214 if (state() == RFBSTATE_NORMAL)
215 writer()->writeServerCutText(str, len);
216 } catch(rdr::Exception& e) {
217 close(e.str());
218 }
219}
220
221void VNCSConnectionST::setCursorOrClose()
222{
223 try {
224 setCursor();
225 } catch(rdr::Exception& e) {
226 close(e.str());
227 }
228}
229
230
231int VNCSConnectionST::checkIdleTimeout()
232{
233 int idleTimeout = rfb::Server::idleTimeout;
234 if (idleTimeout == 0) return 0;
235 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
236 idleTimeout = 15; // minimum of 15 seconds while authenticating
237 time_t now = time(0);
238 if (now < lastEventTime) {
239 // Someone must have set the time backwards. Set lastEventTime so that the
240 // idleTimeout will count from now.
241 vlog.info("Time has gone backwards - resetting idle timeout");
242 lastEventTime = now;
243 }
244 int timeLeft = lastEventTime + idleTimeout - now;
245 if (timeLeft < -60) {
246 // Our callback is over a minute late - someone must have set the time
247 // forwards. Set lastEventTime so that the idleTimeout will count from
248 // now.
249 vlog.info("Time has gone forwards - resetting idle timeout");
250 lastEventTime = now;
251 return idleTimeout;
252 }
253 if (timeLeft <= 0) {
254 close("Idle timeout");
255 return 0;
256 }
257 return timeLeft * 1000;
258}
259
260// renderedCursorChange() is called whenever the server-side rendered cursor
261// changes shape or position. It ensures that the next update will clean up
262// the old rendered cursor and if necessary draw the new rendered cursor.
263
264void VNCSConnectionST::renderedCursorChange()
265{
266 if (state() != RFBSTATE_NORMAL) return;
267 removeRenderedCursor = true;
268 if (needRenderedCursor())
269 drawRenderedCursor = true;
270}
271
272// needRenderedCursor() returns true if this client needs the server-side
273// rendered cursor. This may be because it does not support local cursor or
274// because the current cursor position has not been set by this client.
275// Unfortunately we can't know for sure when the current cursor position has
276// been set by this client. We guess that this is the case when the current
277// cursor position is the same as the last pointer event from this client, or
278// if it is a very short time since this client's last pointer event (up to a
279// second). [ Ideally we should do finer-grained timing here and make the time
280// configurable, but I don't think it's that important. ]
281
282bool VNCSConnectionST::needRenderedCursor()
283{
284 return (state() == RFBSTATE_NORMAL
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000285 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000286 || (!server->cursorPos.equals(pointerEventPos) &&
287 (time(0) - pointerEventTime) > 0)));
288}
289
290
291void VNCSConnectionST::approveConnectionOrClose(bool accept,
292 const char* reason)
293{
294 try {
295 approveConnection(accept, reason);
296 } catch (rdr::Exception& e) {
297 close(e.str());
298 }
299}
300
301
302
303// -=- Callbacks from SConnection
304
305void VNCSConnectionST::versionReceived() {
306 CharArray address(sock->getPeerAddress());
307 if ((rfb::Server::blacklistLevel == 1) &&
308 server->blHosts->isBlackmarked(address.buf)) {
309 server->connectionsLog.error("blacklisted: %s", address.buf);
310 throwConnFailedException("Too many security failures");
311 }
312}
313
314SSecurity* VNCSConnectionST::getSSecurity(int secType) {
315 if (!server->securityFactory)
316 throw rdr::Exception("no SSecurityFactory registered!");
317 return server->securityFactory->getSSecurity(secType, reverseConnection);
318}
319
320void VNCSConnectionST::authSuccess()
321{
322 lastEventTime = time(0);
323
324 // - Authentication succeeded - clear from blacklist
325 CharArray name; name.buf = sock->getPeerAddress();
326 server->blHosts->clearBlackmark(name.buf);
327
328 server->startDesktop();
329
330 // - Set the connection parameters appropriately
331 cp.width = server->pb->width();
332 cp.height = server->pb->height();
333 cp.setName(server->getName());
334
335 // - Set the default pixel format
336 cp.setPF(server->pb->getPF());
337 char buffer[256];
338 cp.pf().print(buffer, 256);
339 vlog.info("Server default pixel format %s", buffer);
340 image_getter.init(server->pb, cp.pf(), 0);
341
342 // - Mark the entire display as "dirty"
343 updates.add_changed(server->pb->getRect());
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000344 startTime = time(0);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000345}
346
347void VNCSConnectionST::queryConnection(const char* userName)
348{
349 // - Does the client have the right to bypass the query?
350 if (reverseConnection || !rfb::Server::queryConnect ||
351 (accessRights & AccessNoQuery))
352 {
353 approveConnection(true);
354 return;
355 }
356
357 CharArray reason;
358 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
359 &reason.buf);
360 if (qr == VNCServerST::PENDING) return;
361 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
362}
363
364void VNCSConnectionST::clientInit(bool shared)
365{
366 lastEventTime = time(0);
367 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
368 if (rfb::Server::neverShared) shared = false;
369 if (!shared) {
370 if (rfb::Server::disconnectClients) {
371 // - Close all the other connected clients
372 vlog.debug("non-shared connection - closing clients");
373 server->closeClients("Non-shared connection requested", getSock());
374 } else {
375 // - Refuse this connection if there are existing clients, in addition to this one
376 if (server->authClientCount() > 1) {
377 close("Server is already in use");
378 return;
379 }
380 }
381 }
382 SConnection::clientInit(shared);
383}
384
385void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
386{
387 SConnection::setPixelFormat(pf);
388 char buffer[256];
389 pf.print(buffer, 256);
390 vlog.info("Client pixel format %s", buffer);
391 image_getter.init(server->pb, pf, writer());
392 setCursor();
393}
394
395void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
396{
397 pointerEventTime = lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000398 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000399 if (!(accessRights & AccessPtrEvents)) return;
400 if (!rfb::Server::acceptPointerEvents) return;
401 if (!server->pointerClient || server->pointerClient == this) {
402 pointerEventPos = Point(x, y);
403 if (buttonMask)
404 server->pointerClient = this;
405 else
406 server->pointerClient = 0;
407 server->desktop->pointerEvent(pointerEventPos, buttonMask);
408 }
409}
410
411
412class VNCSConnectionSTShiftPresser {
413public:
414 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
415 : desktop(desktop_), pressed(false) {}
416 ~VNCSConnectionSTShiftPresser() {
417 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
418 }
419 void press() {
420 desktop->keyEvent(XK_Shift_L, true);
421 pressed = true;
422 }
423 SDesktop* desktop;
424 bool pressed;
425};
426
427// keyEvent() - record in the pressedKeys which keys were pressed. Allow
428// multiple down events (for autorepeat), but only allow a single up event.
429void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
430 lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000431 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000432 if (!(accessRights & AccessKeyEvents)) return;
433 if (!rfb::Server::acceptKeyEvents) return;
434
435 // Turn ISO_Left_Tab into shifted Tab.
436 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
437 if (key == XK_ISO_Left_Tab) {
438 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
439 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
440 shiftPresser.press();
441 key = XK_Tab;
442 }
443
444 if (down) {
445 pressedKeys.insert(key);
446 } else {
447 if (!pressedKeys.erase(key)) return;
448 }
449 server->desktop->keyEvent(key, down);
450}
451
452void VNCSConnectionST::clientCutText(const char* str, int len)
453{
454 if (!(accessRights & AccessCutText)) return;
455 if (!rfb::Server::acceptCutText) return;
456 server->desktop->clientCutText(str, len);
457}
458
459void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
460{
461 if (!(accessRights & AccessView)) return;
462
463 SConnection::framebufferUpdateRequest(r, incremental);
464
465 Region reqRgn(r);
466 requested.assign_union(reqRgn);
467
468 if (!incremental) {
469 // Non-incremental update - treat as if area requested has changed
470 updates.add_changed(reqRgn);
471 server->comparer->add_changed(reqRgn);
472 }
473
474 writeFramebufferUpdate();
475}
476
477void VNCSConnectionST::setInitialColourMap()
478{
479 setColourMapEntries(0, 0);
480}
481
482// supportsLocalCursor() is called whenever the status of
483// cp.supportsLocalCursor has changed. If the client does now support local
484// cursor, we make sure that the old server-side rendered cursor is cleaned up
485// and the cursor is sent to the client.
486
487void VNCSConnectionST::supportsLocalCursor()
488{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000489 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000490 removeRenderedCursor = true;
491 drawRenderedCursor = false;
492 setCursor();
493 }
494}
495
496void VNCSConnectionST::writeSetCursorCallback()
497{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000498 if (cp.supportsLocalXCursor) {
499 Pixel pix0, pix1;
500 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
501 if (bitmap.buf) {
502 // The client supports XCursor and the cursor only has two
503 // colors. Use the XCursor encoding.
504 writer()->writeSetXCursor(server->cursor.width(),
505 server->cursor.height(),
506 server->cursor.hotspot.x,
507 server->cursor.hotspot.y,
508 bitmap.buf, server->cursor.mask.buf);
509 return;
510 } else {
511 // More than two colors
512 if (!cp.supportsLocalCursor) {
513 // FIXME: We could reduce to two colors.
514 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
515 return;
516 }
517 }
518 }
519
520 // Use RichCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000521 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
522 image_getter.translatePixels(server->cursor.data, transData,
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000523 server->cursor.area());
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000524 writer()->writeSetCursor(server->cursor.width(),
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000525 server->cursor.height(),
526 server->cursor.hotspot.x,
527 server->cursor.hotspot.y,
528 transData, server->cursor.mask.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000529}
530
531
532void VNCSConnectionST::writeFramebufferUpdate()
533{
534 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
535
536 server->checkUpdate();
537
538 // If the previous position of the rendered cursor overlaps the source of the
539 // copy, then when the copy happens the corresponding rectangle in the
540 // destination will be wrong, so add it to the changed region.
541
542 if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
543 Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
544 .intersect(server->pb->getRect()));
545 if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
546 updates.add_changed(bogusCopiedCursor);
547 }
548 }
549
550 // If we need to remove the old rendered cursor, just add the rectangle to
551 // the changed region.
552
553 if (removeRenderedCursor) {
554 updates.add_changed(renderedCursorRect);
555 renderedCursorRect.clear();
556 removeRenderedCursor = false;
557 }
558
559 // Return if there is nothing to send the client.
560
561 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
562 return;
563
564 // If the client needs a server-side rendered cursor, work out the cursor
565 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
566 // with the update region, we need to draw the rendered cursor regardless of
567 // whether it has changed.
568
569 if (needRenderedCursor()) {
570 renderedCursorRect
571 = (server->renderedCursor.getRect(server->renderedCursorTL)
572 .intersect(requested.get_bounding_rect()));
573
574 if (renderedCursorRect.is_empty()) {
575 drawRenderedCursor = false;
576 } else if (!updates.get_changed().union_(updates.get_copied())
577 .intersect(renderedCursorRect).is_empty()) {
578 drawRenderedCursor = true;
579 }
580
581 // We could remove the new cursor rect from updates here. It's not clear
582 // whether this is worth it. If we do remove it, then we won't draw over
583 // the same bit of screen twice, but we have the overhead of a more complex
584 // region.
585
586 //if (drawRenderedCursor)
587 // updates.subtract(renderedCursorRect);
588 }
589
590 UpdateInfo update;
591 updates.enable_copyrect(cp.useCopyRect);
592 updates.get_update(&update, requested);
593 if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Peter Åstrand016dc192005-02-10 16:41:06 +0000594 // Compute the number of rectangles. Tight encoder makes the things more
595 // complicated as compared to the original RealVNC.
596 writer()->setupCurrentEncoder();
597 int nRects = update.copied.numRects() + (drawRenderedCursor ? 1 : 0);
598 std::vector<Rect> rects;
599 std::vector<Rect>::const_iterator i;
600 update.changed.get_rects(&rects);
601 for (i = rects.begin(); i != rects.end(); i++) {
602 if (i->width() && i->height())
603 nRects += writer()->getNumRects(*i);
604 }
605
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000606 writer()->writeFramebufferUpdateStart(nRects);
607 Region updatedRegion;
608 writer()->writeRects(update, &image_getter, &updatedRegion);
609 updates.subtract(updatedRegion);
610 if (drawRenderedCursor)
611 writeRenderedCursorRect();
612 writer()->writeFramebufferUpdateEnd();
613 requested.clear();
614 }
615}
616
617
618// writeRenderedCursorRect() writes a single rectangle drawing the rendered
619// cursor on the client.
620
621void VNCSConnectionST::writeRenderedCursorRect()
622{
623 image_getter.setPixelBuffer(&server->renderedCursor);
624 image_getter.setOffset(server->renderedCursorTL);
625
626 Rect actual;
627 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
628
629 image_getter.setPixelBuffer(server->pb);
630 image_getter.setOffset(Point(0,0));
631
632 drawRenderedCursor = false;
633}
634
635void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
636{
637 if (!readyForSetColourMapEntries) return;
638 if (server->pb->getPF().trueColour) return;
639
640 image_getter.setColourMapEntries(firstColour, nColours, writer());
641
642 if (cp.pf().trueColour) {
643 updates.add_changed(server->pb->getRect());
644 }
645}
646
647
648// setCursor() is called whenever the cursor has changed shape or pixel format.
649// If the client supports local cursor then it will arrange for the cursor to
650// be sent to the client.
651
652void VNCSConnectionST::setCursor()
653{
654 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
655 writer()->cursorChange(this);
656 if (writer()->needFakeUpdate())
657 writeFramebufferUpdate();
658}
659
660void VNCSConnectionST::setSocketTimeouts()
661{
662 int timeoutms = rfb::Server::clientWaitTimeMillis;
663 if (timeoutms == 0 || timeoutms > rfb::Server::idleTimeout * 1000) {
664 timeoutms = rfb::Server::idleTimeout * 1000;
665 if (timeoutms == 0)
666 timeoutms = -1;
667 }
668 sock->inStream().setTimeout(timeoutms);
669 sock->outStream().setTimeout(timeoutms);
670}
Oleg Sheikinff43bfd2005-12-07 08:02:52 +0000671
672char* VNCSConnectionST::getStartTime()
673{
674 char* result = ctime(&startTime);
675 result[24] = '\0';
676 return result;
677}
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000678
679void VNCSConnectionST::setStatus(int status)
680{
681 switch (status) {
682 case 0:
683 accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
684 break;
685 case 1:
686 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
687 break;
688 case 2:
689 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
690 break;
691 }
692}
693int VNCSConnectionST::getStatus()
694{
695 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
696 return 0;
697 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
698 return 1;
699 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
700 return 2;
701 return 4;
Dennis Syrovatskyea354642005-12-18 13:10:26 +0000702}
703
704bool VNCSConnectionST::processFTMsg(int type)
705{
706 return false;
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000707}