You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1170 lines
29 KiB
1170 lines
29 KiB
/*
|
|
* This file is called main.c, because it contains most of the new functions
|
|
* for use with LibVNCServer.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#ifdef __STRICT_ANSI__
|
|
#define _BSD_SOURCE
|
|
#endif
|
|
#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>
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
|
|
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 = 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)
|
|
{
|
|
rfbErr("%s: %s\n", str, strerror(errno));
|
|
}
|
|
|
|
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 != 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;
|
|
pthread_t output_thread;
|
|
pthread_create(&output_thread, NULL, clientOutput, (void *)cl);
|
|
|
|
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_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);
|
|
|
|
tv.tv_sec = 60; /* 1 minute */
|
|
tv.tv_usec = 0;
|
|
n = select(cl->sock + 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->sock, &rfds) || FD_ISSET(cl->sock, &efds))
|
|
rfbProcessClientMessage(cl);
|
|
}
|
|
|
|
/* Get rid of the output thread. */
|
|
LOCK(cl->updateMutex);
|
|
TSIGNAL(cl->updateCond);
|
|
UNLOCK(cl->updateMutex);
|
|
IF_PTHREADS(pthread_join(output_thread, NULL));
|
|
|
|
rfbClientConnectionGone(cl);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void*
|
|
listenerRun(void *data)
|
|
{
|
|
rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
|
|
int client_fd;
|
|
struct sockaddr_in peer;
|
|
rfbClientPtr cl;
|
|
socklen_t len;
|
|
|
|
len = sizeof(peer);
|
|
|
|
/* TODO: this thread wont die by restarting the server */
|
|
/* TODO: HTTP is not handled */
|
|
while ((client_fd = accept(screen->listenSock,
|
|
(struct sockaddr*)&peer, &len)) >= 0) {
|
|
cl = rfbNewClient(screen,client_fd);
|
|
len = sizeof(peer);
|
|
|
|
if (cl && !cl->onHold )
|
|
rfbStartOnHoldClient(cl);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
void
|
|
rfbStartOnHoldClient(rfbClientPtr cl)
|
|
{
|
|
pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
|
|
}
|
|
|
|
#else
|
|
|
|
void
|
|
rfbStartOnHoldClient(rfbClientPtr cl)
|
|
{
|
|
cl->onHold = FALSE;
|
|
}
|
|
|
|
#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(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=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->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->httpInitDone=FALSE;
|
|
screen->httpEnableProxyConnect=FALSE;
|
|
screen->httpPort=0;
|
|
screen->httpDir=NULL;
|
|
screen->httpListenSock=-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->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);
|
|
|
|
#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);
|
|
|
|
rfbRRECleanup(screen);
|
|
rfbCoRRECleanup(screen);
|
|
rfbUltraCleanup(screen);
|
|
#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;
|
|
WSAStartup(MAKEWORD(2,2),&trash);
|
|
#endif
|
|
rfbInitSockets(screen);
|
|
rfbHttpInitSockets(screen);
|
|
#ifndef __MINGW32__
|
|
if(screen->ignoreSIGPIPE)
|
|
signal(SIGPIPE,SIG_IGN);
|
|
#endif
|
|
}
|
|
|
|
void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) {
|
|
if(disconnectClients) {
|
|
rfbClientPtr cl;
|
|
rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) )
|
|
if (cl->sock > -1)
|
|
/* we don't care about maxfd here, because the server goes away */
|
|
rfbCloseClient(cl);
|
|
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;
|
|
struct timeval tv;
|
|
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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
clPrev=cl;
|
|
cl=rfbClientIteratorNext(i);
|
|
if(clPrev->sock==-1) {
|
|
rfbClientConnectionGone(clPrev);
|
|
result=TRUE;
|
|
}
|
|
}
|
|
rfbReleaseClientIterator(i);
|
|
|
|
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
|
|
pthread_t listener_thread;
|
|
|
|
screen->backgroundLoop = TRUE;
|
|
|
|
pthread_create(&listener_thread, NULL, listenerRun, screen);
|
|
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);
|
|
}
|