summaryrefslogtreecommitdiffstats
path: root/libvncserver/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libvncserver/main.cpp')
-rw-r--r--libvncserver/main.cpp1376
1 files changed, 1376 insertions, 0 deletions
diff --git a/libvncserver/main.cpp b/libvncserver/main.cpp
new file mode 100644
index 0000000..a07402a
--- /dev/null
+++ b/libvncserver/main.cpp
@@ -0,0 +1,1376 @@
+/*
+ * 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.
+ * All Rights Reserved.
+ *
+ * see GPL (latest version) for full details
+ */
+
+extern "C" {
+ #include <rfb/rfb.h>
+ #include <rfb/rfbregion.h>
+ #include "private.h"
+
+ #include <stdarg.h>
+ #include <errno.h>
+
+ #ifndef false
+ #define false 0
+ #define true -1
+ #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
+
+ #include <signal.h>
+ #include <time.h>
+}
+
+#include <tqobject.h>
+#include <tqvariant.h>
+#include <tqtimer.h>
+#include <tqthread.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;
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+static MUTEX(logMutex);
+static MUTEX(extMutex);
+#endif
+
+static int rfbEnableLogging=1;
+
+#ifdef LIBVNCSERVER_WORDS_BIGENDIAN
+char rfbEndianTest = (1==0);
+#else
+char rfbEndianTest = (1==1);
+#endif
+
+/*
+ * Protocol extensions
+ */
+
+static rfbProtocolExtension* rfbExtensionHead = NULL;
+
+/*
+ * This method registers a list of new extensions.
+ * It avoids same extension getting registered multiple times.
+ * The order is not preserved if multiple extensions are
+ * registered at one-go.
+ */
+void
+rfbRegisterProtocolExtension(rfbProtocolExtension* extension)
+{
+ rfbProtocolExtension *head = rfbExtensionHead, *next = NULL;
+
+ if(extension == NULL)
+ return;
+
+ next = extension->next;
+
+ if (! extMutex_initialized) {
+ INIT_MUTEX(extMutex);
+ extMutex_initialized = 1;
+ }
+
+ LOCK(extMutex);
+
+ while(head != NULL) {
+ if(head == extension) {
+ UNLOCK(extMutex);
+ rfbRegisterProtocolExtension(next);
+ return;
+ }
+
+ head = head->next;
+ }
+
+ extension->next = rfbExtensionHead;
+ rfbExtensionHead = extension;
+
+ UNLOCK(extMutex);
+ rfbRegisterProtocolExtension(next);
+}
+
+/*
+ * This method unregisters a list of extensions.
+ * These extensions won't be available for any new
+ * client connection.
+ */
+void
+rfbUnregisterProtocolExtension(rfbProtocolExtension* extension)
+{
+
+ rfbProtocolExtension *cur = NULL, *pre = NULL;
+
+ if(extension == NULL)
+ return;
+
+ if (! extMutex_initialized) {
+ INIT_MUTEX(extMutex);
+ extMutex_initialized = 1;
+ }
+
+ LOCK(extMutex);
+
+ if(rfbExtensionHead == extension) {
+ rfbExtensionHead = rfbExtensionHead->next;
+ UNLOCK(extMutex);
+ rfbUnregisterProtocolExtension(extension->next);
+ return;
+ }
+
+ cur = pre = rfbExtensionHead;
+
+ while(cur) {
+ if(cur == extension) {
+ pre->next = cur->next;
+ break;
+ }
+ pre = cur;
+ cur = cur->next;
+ }
+
+ UNLOCK(extMutex);
+
+ rfbUnregisterProtocolExtension(extension->next);
+}
+
+rfbProtocolExtension* rfbGetExtensionIterator()
+{
+ if (! extMutex_initialized) {
+ INIT_MUTEX(extMutex);
+ extMutex_initialized = 1;
+ }
+
+ LOCK(extMutex);
+ return rfbExtensionHead;
+}
+
+void rfbReleaseExtensionIterator()
+{
+ UNLOCK(extMutex);
+}
+
+rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension,
+ void* data)
+{
+ rfbExtensionData* extData;
+
+ /* make sure extension is not yet enabled. */
+ for(extData = cl->extensions; extData; extData = extData->next)
+ if(extData->extension == extension)
+ return FALSE;
+
+ extData = (rfbExtensionData*)calloc(sizeof(rfbExtensionData),1);
+ extData->extension = extension;
+ extData->data = data;
+ extData->next = cl->extensions;
+ cl->extensions = extData;
+
+ return TRUE;
+}
+
+rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension)
+{
+ rfbExtensionData* extData;
+ rfbExtensionData* prevData = NULL;
+
+ for(extData = cl->extensions; extData; extData = extData->next) {
+ if(extData->extension == extension) {
+ if(extData->data)
+ free(extData->data);
+ if(prevData == NULL)
+ cl->extensions = extData->next;
+ else
+ prevData->next = extData->next;
+ return TRUE;
+ }
+ prevData = extData;
+ }
+
+ return FALSE;
+}
+
+void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension)
+{
+ rfbExtensionData* data = cl->extensions;
+
+ while(data && data->extension != extension)
+ data = data->next;
+
+ if(data == NULL) {
+ rfbLog("Extension is not enabled !\n");
+ /* rfbCloseClient(cl); */
+ return NULL;
+ }
+
+ return data->data;
+}
+
+/*
+ * Logging
+ */
+
+void rfbLogEnable(int enabled) {
+ rfbEnableLogging=enabled;
+}
+
+/*
+ * rfbLog prints a time-stamped message to the log file (stderr).
+ */
+
+static void
+rfbDefaultLog(const char *format, ...)
+{
+ va_list args;
+ char buf[256];
+ time_t log_clock;
+
+ if(!rfbEnableLogging)
+ return;
+
+ if (! logMutex_initialized) {
+ INIT_MUTEX(logMutex);
+ logMutex_initialized = 1;
+ }
+
+ LOCK(logMutex);
+ va_start(args, format);
+
+ time(&log_clock);
+ strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
+ fprintf(stderr, "%s", buf);
+
+ vfprintf(stderr, format, args);
+ fflush(stderr);
+
+ va_end(args);
+ UNLOCK(logMutex);
+}
+
+rfbLogProc rfbLog=rfbDefaultLog;
+rfbLogProc rfbErr=rfbDefaultLog;
+
+void rfbLogPerror(const char *str)
+{
+#ifdef WIN32
+ wchar_t *s = NULL;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&s, 0, NULL);
+ rfbErr("%s: %S\n", str, s);
+ LocalFree(s);
+#else
+ rfbErr("%s: %s\n", str, strerror(errno));
+#endif
+}
+
+void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
+{
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl;
+
+ iterator=rfbGetClientIterator(rfbScreen);
+ while((cl=rfbClientIteratorNext(iterator))) {
+ LOCK(cl->updateMutex);
+ if(cl->useCopyRect) {
+ sraRegionPtr modifiedRegionBackup;
+ if(!sraRgnEmpty(cl->copyRegion)) {
+ if(cl->copyDX!=dx || cl->copyDY!=dy) {
+ /* if a copyRegion was not yet executed, treat it as a
+ * modifiedRegion. The idea: in this case it could be
+ * source of the new copyRect or modified anyway. */
+ sraRgnOr(cl->modifiedRegion,cl->copyRegion);
+ sraRgnMakeEmpty(cl->copyRegion);
+ } else {
+ /* we have to set the intersection of the source of the copy
+ * and the old copy to modified. */
+ modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
+ sraRgnOffset(modifiedRegionBackup,-dx,-dy);
+ sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
+ sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
+ sraRgnDestroy(modifiedRegionBackup);
+ }
+ }
+
+ sraRgnOr(cl->copyRegion,copyRegion);
+ cl->copyDX = dx;
+ cl->copyDY = dy;
+
+ /* if there were modified regions, which are now copied,
+ * mark them as modified, because the source of these can be overlapped
+ * either by new modified or now copied regions. */
+ modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
+ sraRgnOffset(modifiedRegionBackup,dx,dy);
+ sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
+ sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
+ sraRgnDestroy(modifiedRegionBackup);
+
+ if(!cl->enableCursorShapeUpdates) {
+ /*
+ * n.b. (dx, dy) is the vector pointing in the direction the
+ * copyrect displacement will take place. copyRegion is the
+ * destination rectangle (say), not the source rectangle.
+ */
+ sraRegionPtr cursorRegion;
+ int x = cl->cursorX - cl->screen->cursor->xhot;
+ int y = cl->cursorY - cl->screen->cursor->yhot;
+ int w = cl->screen->cursor->width;
+ int h = cl->screen->cursor->height;
+
+ cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
+ sraRgnAnd(cursorRegion, cl->copyRegion);
+ if(!sraRgnEmpty(cursorRegion)) {
+ /*
+ * current cursor rect overlaps with the copy region *dest*,
+ * mark it as modified since we won't copy-rect stuff to it.
+ */
+ sraRgnOr(cl->modifiedRegion, cursorRegion);
+ }
+ sraRgnDestroy(cursorRegion);
+
+ cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
+ /* displace it to check for overlap with copy region source: */
+ sraRgnOffset(cursorRegion, dx, dy);
+ sraRgnAnd(cursorRegion, cl->copyRegion);
+ if(!sraRgnEmpty(cursorRegion)) {
+ /*
+ * current cursor rect overlaps with the copy region *source*,
+ * mark the *displaced* cursorRegion as modified since we
+ * won't copyrect stuff to it.
+ */
+ sraRgnOr(cl->modifiedRegion, cursorRegion);
+ }
+ sraRgnDestroy(cursorRegion);
+ }
+
+ } else {
+ sraRgnOr(cl->modifiedRegion,copyRegion);
+ }
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ }
+
+ rfbReleaseClientIterator(iterator);
+}
+
+void rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy)
+{
+ sraRectangleIterator* i;
+ sraRect rect;
+ int j,widthInBytes,bpp=screen->serverFormat.bitsPerPixel/8,
+ rowstride=screen->paddedWidthInBytes;
+ char *in,*out;
+
+ /* copy it, really */
+ i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
+ while(sraRgnIteratorNext(i,&rect)) {
+ widthInBytes = (rect.x2-rect.x1)*bpp;
+ out = screen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
+ in = screen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
+ if(dy<0)
+ for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
+ memmove(out,in,widthInBytes);
+ else {
+ out += rowstride*(rect.y2-rect.y1-1);
+ in += rowstride*(rect.y2-rect.y1-1);
+ for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
+ memmove(out,in,widthInBytes);
+ }
+ }
+ sraRgnReleaseIterator(i);
+
+ rfbScheduleCopyRegion(screen,copyRegion,dx,dy);
+}
+
+void rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
+{
+ sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbDoCopyRegion(screen,region,dx,dy);
+ sraRgnDestroy(region);
+}
+
+void rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
+{
+ sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbScheduleCopyRegion(screen,region,dx,dy);
+ sraRgnDestroy(region);
+}
+
+void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion)
+{
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl;
+
+ iterator=rfbGetClientIterator(screen);
+ while((cl=rfbClientIteratorNext(iterator))) {
+ LOCK(cl->updateMutex);
+ sraRgnOr(cl->modifiedRegion,modRegion);
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ }
+
+ rfbReleaseClientIterator(iterator);
+}
+
+void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2);
+void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2)
+{
+ sraRegionPtr region;
+ int i;
+
+ if(x1>x2) { i=x1; x1=x2; x2=i; }
+ if(x1<0) x1=0;
+ if(x2>screen->width) x2=screen->width;
+ if(x1==x2) return;
+
+ if(y1>y2) { i=y1; y1=y2; y2=i; }
+ if(y1<0) y1=0;
+ if(y2>screen->height) y2=screen->height;
+ if(y1==y2) return;
+
+ /* update scaled copies for this rectangle */
+ rfbScaledScreenUpdate(screen,x1,y1,x2,y2);
+
+ region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbMarkRegionAsModified(screen,region);
+ sraRgnDestroy(region);
+}
+
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+#include <unistd.h>
+
+static void *
+clientOutput(void *data)
+{
+ rfbClientPtr cl = (rfbClientPtr)data;
+ rfbBool haveUpdate;
+ sraRegion* updateRegion;
+
+ while (1) {
+ haveUpdate = false;
+ while (!haveUpdate) {
+ if (cl->sock == -1) {
+ /* Client has disconnected. */
+ return NULL;
+ }
+ if (cl->state != _rfbClientRec::RFB_NORMAL || cl->onHold) {
+ /* just sleep until things get normal */
+ usleep(cl->screen->deferUpdateTime * 1000);
+ continue;
+ }
+
+ LOCK(cl->updateMutex);
+
+ if (sraRgnEmpty(cl->requestedRegion)) {
+ ; /* always require a FB Update Request (otherwise can crash.) */
+ } else {
+ haveUpdate = FB_UPDATE_PENDING(cl);
+ if(!haveUpdate) {
+ updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
+ haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
+ sraRgnDestroy(updateRegion);
+ }
+ }
+
+ if (!haveUpdate) {
+ WAIT(cl->updateCond, cl->updateMutex);
+ }
+
+ UNLOCK(cl->updateMutex);
+ }
+
+ /* OK, now, to save bandwidth, wait a little while for more
+ updates to come along. */
+ usleep(cl->screen->deferUpdateTime * 1000);
+
+ /* Now, get the region we're going to update, and remove
+ it from cl->modifiedRegion _before_ we send the update.
+ That way, if anything that overlaps the region we're sending
+ is updated, we'll be sure to do another update later. */
+ LOCK(cl->updateMutex);
+ updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
+ UNLOCK(cl->updateMutex);
+
+ /* Now actually send the update. */
+ rfbIncrClientRef(cl);
+ LOCK(cl->sendMutex);
+ rfbSendFramebufferUpdate(cl, updateRegion);
+ UNLOCK(cl->sendMutex);
+ rfbDecrClientRef(cl);
+
+ sraRgnDestroy(updateRegion);
+ }
+
+ /* Not reached. */
+ return NULL;
+}
+
+static void *
+clientInput(void *data)
+{
+ rfbClientPtr cl = (rfbClientPtr)data;
+ /* 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;
+ struct timeval tv;
+ int n;
+
+ if (cl->sock == -1) {
+ /* Client has disconnected. */
+ break;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(cl->sock, &rfds);
+ FD_SET(cl->pipe_notify_client_thread[0], &rfds);
+ FD_ZERO(&efds);
+ FD_SET(cl->sock, &efds);
+
+ /* Are we transferring a file in the background? */
+ FD_ZERO(&wfds);
+ if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
+ FD_SET(cl->sock, &wfds);
+
+ int nfds = cl->pipe_notify_client_thread[0] > cl->sock ? cl->pipe_notify_client_thread[0] : cl->sock;
+
+ tv.tv_sec = 60; /* 1 minute */
+ tv.tv_usec = 0;
+
+ n = select(nfds + 1, &rfds, &wfds, &efds, &tv);
+
+ if (n < 0) {
+ rfbLogPerror("ReadExact: select");
+ break;
+ }
+ if (n == 0) /* timeout */
+ {
+ rfbSendFileTransferChunk(cl);
+ continue;
+ }
+
+ /* We have some space on the transmit queue, send some data */
+ if (FD_ISSET(cl->sock, &wfds))
+ rfbSendFileTransferChunk(cl);
+
+ if (FD_ISSET(cl->pipe_notify_client_thread[0], &rfds))
+ {
+ /* Reset the pipe */
+ char buf;
+ while (read(cl->pipe_notify_client_thread[0], &buf, sizeof(buf)) == sizeof(buf));
+ }
+
+ if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds))
+ {
+#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
+ do {
+ rfbProcessClientMessage(cl);
+ } while (webSocketsHasDataInBuffer(cl));
+#else
+ rfbProcessClientMessage(cl);
+#endif
+ }
+ }
+
+ /* Get rid of the output thread. */
+ LOCK(cl->updateMutex);
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ clientOutputHandlerThread->wait();
+ delete clientOutputHandlerThread;
+ clientOutputHandlerThread = NULL;
+ delete clientOutputHandler;
+ clientOutputHandler = NULL;
+
+ rfbClientConnectionGone(cl);
+
+ return NULL;
+}
+
+static void*
+listenerRun(void *data)
+{
+ rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
+ int client_fd;
+ struct sockaddr_storage peer;
+ rfbClientPtr cl = NULL;
+ 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) {
+ client_fd = -1;
+ FD_ZERO(&listen_fds);
+ if(screen->listenSock >= 0)
+ FD_SET(screen->listenSock, &listen_fds);
+ if(screen->listen6Sock >= 0)
+ FD_SET(screen->listen6Sock, &listen_fds);
+
+ if (select(screen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) {
+ rfbLogPerror("listenerRun: error in select");
+ return NULL;
+ }
+
+ /* there is something on the listening sockets, handle new connections */
+ len = sizeof (peer);
+ if (FD_ISSET(screen->listenSock, &listen_fds))
+ client_fd = accept(screen->listenSock, (struct sockaddr*)&peer, &len);
+ else if (FD_ISSET(screen->listen6Sock, &listen_fds))
+ client_fd = accept(screen->listen6Sock, (struct sockaddr*)&peer, &len);
+
+ if(client_fd >= 0)
+ cl = rfbNewClient(screen,client_fd);
+ if (cl && !cl->onHold )
+ rfbStartOnHoldClient(cl);
+ }
+ return(NULL);
+}
+
+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)
+{
+ cl->onHold = FALSE;
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+ if(cl->screen->backgroundLoop) {
+ if (pipe(cl->pipe_notify_client_thread) == -1) {
+ cl->pipe_notify_client_thread[0] = -1;
+ cl->pipe_notify_client_thread[1] = -1;
+ }
+ fcntl(cl->pipe_notify_client_thread[0], F_SETFL, O_NONBLOCK);
+
+ pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
+ }
+#endif
+}
+
+#endif
+
+void
+rfbRefuseOnHoldClient(rfbClientPtr cl)
+{
+ rfbCloseClient(cl);
+ rfbClientConnectionGone(cl);
+}
+
+static void
+rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
+{
+}
+
+void
+rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
+{
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr other_client;
+ rfbScreenInfoPtr s = cl->screen;
+
+ if (x != s->cursorX || y != s->cursorY) {
+ LOCK(s->cursorMutex);
+ s->cursorX = x;
+ s->cursorY = y;
+ UNLOCK(s->cursorMutex);
+
+ /* The cursor was moved by this client, so don't send CursorPos. */
+ if (cl->enableCursorPosUpdates)
+ cl->cursorWasMoved = FALSE;
+
+ /* But inform all remaining clients about this cursor movement. */
+ iterator = rfbGetClientIterator(s);
+ while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
+ if (other_client != cl && other_client->enableCursorPosUpdates) {
+ other_client->cursorWasMoved = TRUE;
+ }
+ }
+ rfbReleaseClientIterator(iterator);
+ }
+}
+
+static void rfbDefaultSetXCutText(char* text, int len, rfbClientPtr cl)
+{
+}
+
+/* TODO: add a nice VNC or RFB cursor */
+
+#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI)
+static rfbCursor myCursor =
+{
+ FALSE, FALSE, FALSE, FALSE,
+ (unsigned char*)"\000\102\044\030\044\102\000",
+ (unsigned char*)"\347\347\176\074\176\347\347",
+ 8, 7, 3, 3,
+ 0, 0, 0,
+ 0xffff, 0xffff, 0xffff,
+ NULL
+};
+#else
+static rfbCursor myCursor =
+{
+ cleanup: FALSE,
+ cleanupSource: FALSE,
+ cleanupMask: FALSE,
+ cleanupRichSource: FALSE,
+ source: "\000\102\044\030\044\102\000",
+ mask: "\347\347\176\074\176\347\347",
+ width: 8, height: 7, xhot: 3, yhot: 3,
+ foreRed: 0, foreGreen: 0, foreBlue: 0,
+ backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
+ richSource: NULL
+};
+#endif
+
+static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl)
+{
+ return(cl->screen->cursor);
+}
+
+/* response is cl->authChallenge vncEncrypted with passwd */
+static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
+{
+ int i;
+ char *passwd=rfbDecryptPasswdFromFile((char*)cl->screen->authPasswdData);
+
+ if(!passwd) {
+ rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData);
+ return(FALSE);
+ }
+
+ rfbEncryptBytes(cl->authChallenge, passwd);
+
+ /* Lose the password from memory */
+ for (i = strlen(passwd); i >= 0; i--) {
+ passwd[i] = '\0';
+ }
+
+ free(passwd);
+
+ if (memcmp(cl->authChallenge, response, len) != 0) {
+ rfbErr("authProcessClientMessage: authentication failed from %s\n",
+ cl->host);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+/* for this method, authPasswdData is really a pointer to an array
+ of char*'s, where the last pointer is 0. */
+rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len)
+{
+ char **passwds;
+ int i=0;
+
+ for(passwds=(char**)cl->screen->authPasswdData;*passwds;passwds++,i++) {
+ uint8_t auth_tmp[CHALLENGESIZE];
+ memcpy((char *)auth_tmp, (char *)cl->authChallenge, CHALLENGESIZE);
+ rfbEncryptBytes(auth_tmp, *passwds);
+
+ if (memcmp(auth_tmp, response, len) == 0) {
+ if(i>=cl->screen->authPasswdFirstViewOnly)
+ cl->viewOnly=TRUE;
+ return(TRUE);
+ }
+ }
+
+ rfbErr("authProcessClientMessage: authentication failed from %s\n",
+ cl->host);
+ return(FALSE);
+}
+
+void rfbDoNothingWithClient(rfbClientPtr cl)
+{
+}
+
+static enum rfbNewClientAction rfbDefaultNewClientHook(rfbClientPtr cl)
+{
+ return RFB_CLIENT_ACCEPT;
+}
+
+/*
+ * Update server's pixel format in screenInfo structure. This
+ * function is called from rfbGetScreen() and rfbNewFramebuffer().
+ */
+
+static void rfbInitServerFormat(rfbScreenInfoPtr screen, int bitsPerSample)
+{
+ rfbPixelFormat* format=&screen->serverFormat;
+
+ format->bitsPerPixel = screen->bitsPerPixel;
+ format->depth = screen->depth;
+ format->bigEndian = rfbEndianTest?FALSE:TRUE;
+ format->trueColour = TRUE;
+ screen->colourMap.count = 0;
+ screen->colourMap.is16 = 0;
+ screen->colourMap.data.bytes = NULL;
+
+ if (format->bitsPerPixel == 8) {
+ format->redMax = 7;
+ format->greenMax = 7;
+ format->blueMax = 3;
+ format->redShift = 0;
+ format->greenShift = 3;
+ format->blueShift = 6;
+ } else {
+ format->redMax = (1 << bitsPerSample) - 1;
+ format->greenMax = (1 << bitsPerSample) - 1;
+ format->blueMax = (1 << bitsPerSample) - 1;
+ if(rfbEndianTest) {
+ format->redShift = 0;
+ format->greenShift = bitsPerSample;
+ format->blueShift = bitsPerSample * 2;
+ } else {
+ if(format->bitsPerPixel==8*3) {
+ format->redShift = bitsPerSample*2;
+ format->greenShift = bitsPerSample*1;
+ format->blueShift = 0;
+ } else {
+ format->redShift = bitsPerSample*3;
+ format->greenShift = bitsPerSample*2;
+ format->blueShift = bitsPerSample;
+ }
+ }
+ }
+}
+
+rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
+ int width,int height,int bitsPerSample,int samplesPerPixel,
+ int bytesPerPixel)
+{
+ rfbScreenInfoPtr screen=(rfbScreenInfoPtr)calloc(sizeof(rfbScreenInfo),1);
+
+ if (! logMutex_initialized) {
+ INIT_MUTEX(logMutex);
+ logMutex_initialized = 1;
+ }
+
+
+ if(width&3)
+ rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);
+
+ screen->autoPort=FALSE;
+ screen->clientHead=NULL;
+ screen->pointerClient=NULL;
+ screen->port=5900;
+ screen->ipv6port=5900;
+ screen->socketState=RFB_SOCKET_INIT;
+
+ screen->inetdInitDone = FALSE;
+ screen->inetdSock=-1;
+
+ screen->udpSock=-1;
+ screen->udpSockConnected=FALSE;
+ screen->udpPort=0;
+ screen->udpClient=NULL;
+
+ screen->maxFd=0;
+ screen->listenSock=-1;
+ screen->listen6Sock=-1;
+
+ screen->httpInitDone=FALSE;
+ screen->httpEnableProxyConnect=FALSE;
+ screen->httpPort=0;
+ screen->http6Port=0;
+ screen->httpDir=NULL;
+ screen->httpListenSock=-1;
+ screen->httpListen6Sock=-1;
+ screen->httpSock=-1;
+
+ screen->desktopName = "LibVNCServer";
+ screen->alwaysShared = FALSE;
+ screen->neverShared = FALSE;
+ screen->dontDisconnect = FALSE;
+ screen->authPasswdData = NULL;
+ screen->authPasswdFirstViewOnly = 1;
+
+ screen->width = width;
+ screen->height = height;
+ screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
+
+ screen->passwordCheck = rfbDefaultPasswordCheck;
+
+ screen->ignoreSIGPIPE = TRUE;
+
+ /* disable progressive updating per default */
+ screen->progressiveSliceHeight = 0;
+
+ screen->listenInterface = htonl(INADDR_ANY);
+
+ screen->deferUpdateTime=5;
+ screen->maxRectsPerUpdate=50;
+
+ screen->handleEventsEagerly = FALSE;
+
+ screen->protocolMajorVersion = rfbProtocolMajorVersion;
+ screen->protocolMinorVersion = rfbProtocolMinorVersion;
+
+ screen->permitFileTransfer = FALSE;
+
+ if(!rfbProcessArguments(screen,argc,argv)) {
+ free(screen);
+ return NULL;
+ }
+
+#ifdef WIN32
+ {
+ DWORD dummy=255;
+ GetComputerName(screen->thisHost,&dummy);
+ }
+#else
+ gethostname(screen->thisHost, 255);
+#endif
+
+ screen->paddedWidthInBytes = width*bytesPerPixel;
+
+ /* format */
+
+ rfbInitServerFormat(screen, bitsPerSample);
+
+ /* cursor */
+
+ screen->cursorX=screen->cursorY=screen->underCursorBufferLen=0;
+ screen->underCursorBuffer=NULL;
+ screen->dontConvertRichCursorToXCursor = FALSE;
+ screen->cursor = &myCursor;
+ INIT_MUTEX(screen->cursorMutex);
+
+ IF_PTHREADS(screen->backgroundLoop = FALSE);
+
+ /* proc's and hook's */
+
+ screen->kbdAddEvent = rfbDefaultKbdAddEvent;
+ screen->kbdReleaseAllKeys = rfbDoNothingWithClient;
+ screen->ptrAddEvent = rfbDefaultPtrAddEvent;
+ screen->setXCutText = rfbDefaultSetXCutText;
+ screen->getCursorPtr = rfbDefaultGetCursorPtr;
+ screen->setTranslateFunction = rfbSetTranslateFunction;
+ screen->newClientHook = rfbDefaultNewClientHook;
+ screen->displayHook = NULL;
+ screen->inetdDisconnectHook = NULL;
+ screen->displayFinishedHook = NULL;
+ screen->getKeyboardLedStateHook = NULL;
+ screen->xvpHook = NULL;
+
+ /* initialize client list and iterator mutex */
+ rfbClientListInit(screen);
+
+ return(screen);
+}
+
+/*
+ * Switch to another framebuffer (maybe of different size and color
+ * format). Clients supporting NewFBSize pseudo-encoding will change
+ * their local framebuffer dimensions if necessary.
+ * NOTE: Rich cursor data should be converted to new pixel format by
+ * the caller.
+ */
+
+void rfbNewFramebuffer(rfbScreenInfoPtr screen, char *framebuffer,
+ int width, int height,
+ int bitsPerSample, int samplesPerPixel,
+ int bytesPerPixel)
+{
+ rfbPixelFormat old_format;
+ rfbBool format_changed = FALSE;
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl;
+
+ /* Update information in the screenInfo structure */
+
+ old_format = screen->serverFormat;
+
+ if (width & 3)
+ rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width);
+
+ screen->width = width;
+ screen->height = height;
+ screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
+ screen->paddedWidthInBytes = width*bytesPerPixel;
+
+ rfbInitServerFormat(screen, bitsPerSample);
+
+ if (memcmp(&screen->serverFormat, &old_format,
+ sizeof(rfbPixelFormat)) != 0) {
+ format_changed = TRUE;
+ }
+
+ screen->frameBuffer = framebuffer;
+
+ /* Adjust pointer position if necessary */
+
+ if (screen->cursorX >= width)
+ screen->cursorX = width - 1;
+ if (screen->cursorY >= height)
+ screen->cursorY = height - 1;
+
+ /* For each client: */
+ iterator = rfbGetClientIterator(screen);
+ while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
+
+ /* Re-install color translation tables if necessary */
+
+ if (format_changed)
+ screen->setTranslateFunction(cl);
+
+ /* Mark the screen contents as changed, and schedule sending
+ NewFBSize message if supported by this client. */
+
+ LOCK(cl->updateMutex);
+ sraRgnDestroy(cl->modifiedRegion);
+ cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height);
+ sraRgnMakeEmpty(cl->copyRegion);
+ cl->copyDX = 0;
+ cl->copyDY = 0;
+
+ if (cl->useNewFBSize)
+ cl->newFBSizePending = TRUE;
+
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ }
+ rfbReleaseClientIterator(iterator);
+}
+
+/* hang up on all clients and free all reserved memory */
+
+void rfbScreenCleanup(rfbScreenInfoPtr screen)
+{
+ rfbClientIteratorPtr i=rfbGetClientIterator(screen);
+ rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
+ while(cl1) {
+ cl=rfbClientIteratorNext(i);
+ rfbClientConnectionGone(cl1);
+ 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);
+ TINI_MUTEX(screen->cursorMutex);
+ if(screen->cursor && screen->cursor->cleanup)
+ rfbFreeCursor(screen->cursor);
+
+#ifdef LIBVNCSERVER_HAVE_LIBZ
+ rfbZlibCleanup(screen);
+#ifdef LIBVNCSERVER_HAVE_LIBJPEG
+ rfbTightCleanup(screen);
+#endif
+
+ /* free all 'scaled' versions of this screen */
+ while (screen->scaledScreenNext!=NULL)
+ {
+ rfbScreenInfoPtr ptr;
+ ptr = screen->scaledScreenNext;
+ screen->scaledScreenNext = ptr->scaledScreenNext;
+ free(ptr->frameBuffer);
+ free(ptr);
+ }
+
+#endif
+ free(screen);
+}
+
+void rfbInitServer(rfbScreenInfoPtr screen)
+{
+#ifdef WIN32
+ WSADATA trash;
+ static rfbBool WSAinitted=FALSE;
+ if(!WSAinitted) {
+ int i=WSAStartup(MAKEWORD(2,0),&trash);
+ if(i!=0) {
+ rfbErr("Couldn't init Windows Sockets\n");
+ return;
+ }
+ WSAinitted=TRUE;
+ }
+#endif
+ rfbInitSockets(screen);
+ rfbHttpInitSockets(screen);
+#ifndef WIN32
+ if(screen->ignoreSIGPIPE)
+ signal(SIGPIPE,SIG_IGN);
+#endif
+}
+
+void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) {
+ if(disconnectClients) {
+ rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
+ rfbClientPtr nextCl, currentCl = rfbClientIteratorNext(iter);
+
+ while(currentCl) {
+ nextCl = rfbClientIteratorNext(iter);
+ if (currentCl->sock > -1) {
+ /* we don't care about maxfd here, because the server goes away */
+ rfbCloseClient(currentCl);
+ }
+
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+ /*
+ Notify the thread. This simply writes a NULL byte to the notify pipe in order to get past the select()
+ in clientInput(), the loop in there will then break because the rfbCloseClient() above has set
+ currentCl->sock to -1.
+ */
+ write(currentCl->pipe_notify_client_thread[1], "\x00", 1);
+ /* And wait for it to finish. */
+ pthread_join(currentCl->client_thread, NULL);
+#else
+ rfbClientConnectionGone(currentCl);
+#endif
+
+ currentCl = nextCl;
+ }
+
+ rfbReleaseClientIterator(iter);
+ }
+
+ rfbShutdownSockets(screen);
+ rfbHttpShutdownSockets(screen);
+}
+
+#ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY
+#include <fcntl.h>
+#include <conio.h>
+#include <sys/timeb.h>
+
+void gettimeofday(struct timeval* tv,char* dummy)
+{
+ SYSTEMTIME t;
+ GetSystemTime(&t);
+ tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
+ tv->tv_usec=t.wMilliseconds*1000;
+}
+#endif
+
+rfbBool
+rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
+{
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl,clPrev;
+ rfbBool result=FALSE;
+ extern rfbClientIteratorPtr
+ rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);
+
+ if(usec<0)
+ usec=screen->deferUpdateTime*1000;
+
+ rfbCheckFds(screen,usec);
+ rfbHttpCheckFds(screen);
+
+ i = rfbGetClientIteratorWithClosed(screen);
+ cl=rfbClientIteratorHead(i);
+ while(cl) {
+ result = rfbUpdateClient(cl);
+ clPrev=cl;
+ cl=rfbClientIteratorNext(i);
+ if(clPrev->sock==-1) {
+ rfbClientConnectionGone(clPrev);
+ result=TRUE;
+ }
+ }
+ rfbReleaseClientIterator(i);
+
+ return result;
+}
+
+rfbBool
+rfbUpdateClient(rfbClientPtr cl)
+{
+ struct timeval tv;
+ rfbBool result=FALSE;
+ rfbScreenInfoPtr screen = cl->screen;
+
+ if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) &&
+ !sraRgnEmpty(cl->requestedRegion)) {
+ result=TRUE;
+ if(screen->deferUpdateTime == 0) {
+ rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
+ } else if(cl->startDeferring.tv_usec == 0) {
+ gettimeofday(&cl->startDeferring,NULL);
+ if(cl->startDeferring.tv_usec == 0)
+ cl->startDeferring.tv_usec++;
+ } else {
+ gettimeofday(&tv,NULL);
+ if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
+ || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
+ +(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
+ > screen->deferUpdateTime) {
+ cl->startDeferring.tv_usec = 0;
+ rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
+ }
+ }
+ }
+
+ if (!cl->viewOnly && cl->lastPtrX >= 0) {
+ if(cl->startPtrDeferring.tv_usec == 0) {
+ gettimeofday(&cl->startPtrDeferring,NULL);
+ if(cl->startPtrDeferring.tv_usec == 0)
+ cl->startPtrDeferring.tv_usec++;
+ } else {
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ if(tv.tv_sec < cl->startPtrDeferring.tv_sec /* at midnight */
+ || ((tv.tv_sec-cl->startPtrDeferring.tv_sec)*1000
+ +(tv.tv_usec-cl->startPtrDeferring.tv_usec)/1000)
+ > cl->screen->deferPtrUpdateTime) {
+ cl->startPtrDeferring.tv_usec = 0;
+ cl->screen->ptrAddEvent(cl->lastPtrButtons,
+ cl->lastPtrX,
+ cl->lastPtrY, cl);
+ cl->lastPtrX = -1;
+ }
+ }
+ }
+
+ return result;
+}
+
+rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo) {
+ return screenInfo->socketState!=RFB_SOCKET_SHUTDOWN || screenInfo->clientHead!=NULL;
+}
+
+void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground)
+{
+ if(runInBackground) {
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+ 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;
+#endif
+ }
+
+ if(usec<0)
+ usec=screen->deferUpdateTime*1000;
+
+ 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"