This monster commit contains support for TightVNC's file transfer protocol.

Thank you very much, Rohit!
pull/1/head
dscho 18 years ago
parent 93be927b1c
commit 0a909fde7a

@ -19,6 +19,9 @@ original proof-of-concept. It really deserves to replace the old version,
as it is a state-of-the-art, fast and usable program by now! However, he
maintains it and improves it still in amazing ways!
The file transfer protocol from TightVNC was implemented by Rohit Kumar.
This includes an implementation of RFB protocol version 3.7t.
Occasional important patches were sent by (in order I found the names in my
archives and please don't beat me, if I forgot you, but just send me an
email!): Akira Hatakeyama, Karl J. Runge, Justin "Zippy" Dearing,

@ -1,3 +1,9 @@
2005-09-28 Rohit Kumar <rokumar@novell.com>
* examples/filetransfer.c, rfb/rfb.h, configure.ac,
libvncserver/{auth,cargs,main,rfbserver,sockets}.c,
libvncserver/tightvnc-extension/*:
Implement TightVNC's file transfer protocol.
2005-09-27 Rohit Kumar <rokumar@novell.com>
* libvncserver/{cargs,sockets,main,rfbserver}.c,
rfb/rfb.h: Provide a generic means to extend the RFB

@ -17,6 +17,14 @@ AC_PATH_PROG([AR], [ar], [/usr/bin/ar],
[$PATH:/usr/ccs/bin])
# Options
AH_TEMPLATE(WITH_TIGHTVNC_FILETRANSFER, [Disable TightVNCFileTransfer protocol])
AC_ARG_WITH(tightvnc-filetransfer,
[ --without-filetransfer disable TightVNC file transfer protocol],
, [ with_tightvnc_filetransfer=yes ])
if test "x$with_tightvnc_filetransfer" == "xyes"; then
AC_DEFINE(WITH_TIGHTVNC_FILETRANSFER)
fi
AM_CONDITIONAL(WITH_TIGHTVNC_FILETRANSFER, test "$with_tightvnc_filetransfer" == "yes")
AH_TEMPLATE(BACKCHANNEL, [Enable BackChannel communication])
AC_ARG_WITH(backchannel,
[ --without-backchannel disable backchannel method],

@ -13,4 +13,5 @@ simple15
colourmaptest
regiontest
mac
filetransfer

@ -6,9 +6,14 @@ MAC=mac
mac_LDFLAGS=-framework ApplicationServices -framework Carbon -framework IOKit
endif
if WITH_TIGHTVNC_FILETRANSFER
FILETRANSFER=filetransfer
endif
noinst_HEADERS=radon.h
noinst_PROGRAMS=example pnmshow regiontest pnmshow24 fontsel \
vncev storepasswd colourmaptest simple simple15 $(MAC)
vncev storepasswd colourmaptest simple simple15 $(MAC) \
$(FILETRANSFER)

@ -0,0 +1,11 @@
#include <rfb/rfb.h>
int main(int argc,char** argv)
{
rfbScreenInfoPtr server=rfbGetScreen(&argc,argv,400,300,8,3,4);
server->frameBuffer=(char*)malloc(400*300*4);
rfbRegisterTightVNCFileTransferExtension();
rfbInitServer(server);
rfbRunEventLoop(server,-1,FALSE);
return(0);
}

@ -1,5 +1,5 @@
.deps
Makefile
Makefile.in
client_test
libvncclient.a

@ -1,4 +1,5 @@
.deps
Makefile
Makefile.in
libvncserver.a

@ -1,4 +1,16 @@
DEFINES=-g -Wall
AM_CFLAGS=-g -Wall
#if WITH_TIGHTVNC_FILETRANSFER
TIGHTVNCFILETRANSFERHDRS=tightvnc-filetransfer/filelistinfo.h \
tightvnc-filetransfer/filetransfermsg.h \
tightvnc-filetransfer/handlefiletransferrequest.h \
tightvnc-filetransfer/rfbtightproto.h
TIGHTVNCFILETRANSFERSRCS = tightvnc-filetransfer/rfbtightserver.c \
tightvnc-filetransfer/handlefiletransferrequest.c \
tightvnc-filetransfer/filetransfermsg.c \
tightvnc-filetransfer/filelistinfo.c
#endif
includedir=$(prefix)/include/rfb
#include_HEADERS=rfb.h rfbconfig.h rfbint.h rfbproto.h keysym.h rfbregion.h
@ -7,7 +19,8 @@ include_HEADERS=../rfb/rfb.h ../rfb/rfbconfig.h ../rfb/rfbint.h \
../rfb/rfbproto.h ../rfb/keysym.h ../rfb/rfbregion.h ../rfb/rfbclient.h
noinst_HEADERS=d3des.h ../rfb/default8x16.h zrleoutstream.h \
zrlepalettehelper.h zrletypes.h private.h
zrlepalettehelper.h zrletypes.h private.h \
$(TIGHTVNCFILETRANSFERHDRS)
EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \
tableinitcmtemplate.c tabletrans24template.c \
@ -24,7 +37,7 @@ LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \
stats.c corre.c hextile.c rre.c translate.c cutpaste.c \
httpd.c cursor.c font.c \
draw.c selbox.c d3des.c vncauth.c cargs.c \
$(ZLIBSRCS) $(JPEGSRCS)
$(ZLIBSRCS) $(JPEGSRCS) $(TIGHTVNCFILETRANSFERSRCS)
libvncserver_a_SOURCES=$(LIB_SRCS)

@ -207,7 +207,7 @@ rfbAuthNewClient(rfbClientPtr cl)
void
rfbProcessClientSecurityType(rfbClientPtr cl)
{
int n, i;
int n;
uint8_t chosenType;
rfbSecurityHandler* handler;

@ -151,7 +151,7 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[])
for(extension=rfbGetExtensionIterator();handled==0 && extension;
extension=extension->next)
if(extension->processArgument)
handled = extension->processArgument(argv + i);
handled = extension->processArgument(*argc - i, argv + i);
rfbReleaseExtensionIterator();
if(handled==0) {

@ -84,6 +84,46 @@ 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;
}
/*
* Logging
*/

@ -229,6 +229,7 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
rfbClientPtr cl,cl_;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
rfbProtocolExtension* extension;
cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1);
@ -361,6 +362,16 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
}
}
for(extension = rfbGetExtensionIterator(); extension;
extension=extension->next) {
void* data = NULL;
/* if the extension does not have a newClient method, it wants
* to be initialized later. */
if(extension->newClient && extension->newClient(cl, &data))
rfbEnableExtension(cl, extension, data);
}
rfbReleaseExtensionIterator();
switch (cl->screen->newClientHook(cl)) {
case RFB_CLIENT_ON_HOLD:
cl->onHold = TRUE;
@ -606,7 +617,7 @@ rfbProcessClientInitMessage(rfbClientPtr cl)
int len, n;
rfbClientIteratorPtr iterator;
rfbClientPtr otherCl;
rfbProtocolExtension* extension;
rfbExtensionData* extension;
if ((n = rfbReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) {
if (n == 0)
@ -636,18 +647,14 @@ rfbProcessClientInitMessage(rfbClientPtr cl)
return;
}
for(extension=rfbGetExtensionIterator();extension;extension=extension->next)
if(extension->init) {
void* data;
if(extension->init(cl, &data)) {
rfbExtensionData* extensionData=calloc(sizeof(rfbExtensionData),1);
extensionData->extension=extension;
extensionData->data=data;
extensionData->next=cl->extensions;
cl->extensions=extensionData;
}
}
rfbReleaseExtensionIterator();
for(extension = cl->extensions; extension;) {
rfbExtensionData* next = extension->next;
if(extension->extension->init &&
!extension->extension->init(cl, extension->data))
/* extension requested that it be removed */
rfbDisableExtension(cl, extension->extension);
extension = next;
}
cl->state = RFB_NORMAL;
@ -1068,7 +1075,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
for(extension=cl->extensions; extension; extension=extension->next)
if(extension->extension->handleMessage &&
extension->extension->handleMessage(cl, extension->data, msg))
extension->extension->handleMessage(cl, extension->data, &msg))
return;
if(cl->screen->processCustomClientMessage(cl,msg.type)) {

@ -466,6 +466,14 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
}
}
}
#undef DEBUG_READ_EXACT
#ifdef DEBUG_READ_EXACT
rfbLog("ReadExact %d bytes\n",len);
for(n=0;n<len;n++)
fprintf(stderr,"%02x ",(unsigned char)buf[n]);
fprintf(stderr,"\n");
#endif
return 1;
}

@ -0,0 +1,15 @@
DEFINES=-g -Wall
includedir=$(prefix)/include/rfb
noinst_HEADERS=filelistinfo.h filetransfermsg.h \
handlefiletransferrequest.h rfbtightproto.h
LIB_SRCS = rfbtightserver.c handlefiletransferrequest.c filetransfermsg.c \
filelistinfo.c
tightvnc_filetransfer_a_SOURCES=$(LIB_SRCS)
lib_LIBRARIES=tightvnc-filetransfer.a

@ -0,0 +1,130 @@
/*
* Copyright (c) 2005 Novell, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com
*
* Author : Rohit Kumar
* Email ID : rokumar@novell.com
* Date : 14th July 2005
*/
#include <stdio.h>
#include "rfb/rfb.h"
#include "filelistinfo.h"
/* This method is used for debugging purpose */
void
DisplayFileList(FileListInfo fli)
{
int i = 0;
if((fli.pEntries == NULL) || (fli.numEntries == 0)) return;
rfbLog("DISPLAYING FILE NAMES IN THE LIST ...START\n\n");
rfbLog("Numer of entries:: %d\n", fli.numEntries);
for(i = 0; i < fli.numEntries; i++)
rfbLog("file[%d]\t<%s>\n", i, fli.pEntries[i].name);
rfbLog("DISPLAYING FILE NAMES IN THE LIST ...END\n\n");
}
int
AddFileListItemInfo(FileListInfoPtr fileListInfoPtr, char* name,
unsigned int size, unsigned int data)
{
FileListItemInfoPtr fileListItemInfoPtr = (FileListItemInfoPtr)
calloc((fileListInfoPtr->numEntries + 1),
sizeof(FileListItemInfo));
if(fileListItemInfoPtr == NULL) {
rfbLog("File [%s]: Method [%s]: fileListItemInfoPtr is NULL\n",
__FILE__, __FUNCTION__);
return FAILURE;
}
if(fileListInfoPtr->numEntries != 0) {
memcpy(fileListItemInfoPtr, fileListInfoPtr->pEntries,
fileListInfoPtr->numEntries * sizeof(FileListItemInfo));
}
strcpy(fileListItemInfoPtr[fileListInfoPtr->numEntries].name, name);
fileListItemInfoPtr[fileListInfoPtr->numEntries].size = size;
fileListItemInfoPtr[fileListInfoPtr->numEntries].data = data;
if(fileListInfoPtr->pEntries != NULL) {
free(fileListInfoPtr->pEntries);
fileListInfoPtr->pEntries = NULL;
}
fileListInfoPtr->pEntries = fileListItemInfoPtr;
fileListItemInfoPtr = NULL;
fileListInfoPtr->numEntries++;
return SUCCESS;
}
char*
GetFileNameAt(FileListInfo fileListInfo, int number)
{
char* name = NULL;
if(number >= 0 && number < fileListInfo.numEntries)
name = fileListInfo.pEntries[number].name;
return name;
}
unsigned int
GetFileSizeAt(FileListInfo fileListInfo, int number)
{
unsigned int size = 0;
if(number >= 0 && number < fileListInfo.numEntries)
size = fileListInfo.pEntries[number].size;
return size;
}
unsigned int
GetFileDataAt(FileListInfo fileListInfo, int number)
{
unsigned int data = 0;
if(number >= 0 && number < fileListInfo.numEntries)
data = fileListInfo.pEntries[number].data;
return data;
}
unsigned int
GetSumOfFileNamesLength(FileListInfo fileListInfo)
{
int i = 0, sumLen = 0;
for(i = 0; i < fileListInfo.numEntries; i++)
sumLen += strlen(fileListInfo.pEntries[i].name);
return sumLen;
}
void
FreeFileListInfo(FileListInfo fileListInfo)
{
if(fileListInfo.pEntries != NULL) {
free(fileListInfo.pEntries);
fileListInfo.pEntries = NULL;
}
fileListInfo.numEntries = 0;
}

@ -0,0 +1,61 @@
/*
* Copyright (c) 2005 Novell, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com
*
* Author : Rohit Kumar
* Email ID : rokumar@novell.com
* Date : 14th July 2005
*/
#ifndef FILE_LIST_INFO_H
#define FILE_LIST_INFO_H
#include <limits.h>
#define SUCCESS 1
#define FAILURE 0
typedef struct _FileListItemInfo {
char name[NAME_MAX];
unsigned int size;
unsigned int data;
} FileListItemInfo, *FileListItemInfoPtr;
typedef struct _FileListItemSize {
unsigned int size;
unsigned int data;
} FileListItemSize, *FileListItemSizePtr;
typedef struct _FileListInfo {
FileListItemInfoPtr pEntries;
int numEntries;
} FileListInfo, *FileListInfoPtr;
int AddFileListItemInfo(FileListInfoPtr fileListInfoPtr, char* name, unsigned int size, unsigned int data);
char* GetFileNameAt(FileListInfo fileListInfo, int number);
unsigned int GetFileSizeAt(FileListInfo fileListInfo, int number);
unsigned int GetFileDataAt(FileListInfo fileListInfo, int number);
unsigned int GetSumOfFileNamesLength(FileListInfo fileListInfo);
void FreeFileListInfo(FileListInfo fileListInfo);
void DisplayFileList(FileListInfo fli);
#endif

@ -0,0 +1,632 @@
/*
* Copyright (c) 2005 Novell, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com
*
* Author : Rohit Kumar
* Email ID : rokumar@novell.com
* Date : 14th July 2005
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <utime.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <rfb/rfb.h>
#include "rfbtightproto.h"
#include "filelistinfo.h"
#include "filetransfermsg.h"
#include "handlefiletransferrequest.h"
#define SZ_RFBBLOCKSIZE 8192
void
FreeFileTransferMsg(FileTransferMsg ftm)
{
if(ftm.data != NULL) {
free(ftm.data);
ftm.data = NULL;
}
ftm.length = 0;
}
/******************************************************************************
* Methods to handle file list request.
******************************************************************************/
int CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag);
FileTransferMsg CreateFileListErrMsg(char flags);
FileTransferMsg CreateFileListMsg(FileListInfo fileListInfo, char flags);
/*
* This is the method called by HandleFileListRequest to get the file list
*/
FileTransferMsg
GetFileListResponseMsg(char* path, char flags)
{
FileTransferMsg fileListMsg;
FileListInfo fileListInfo;
int status = -1;
memset(&fileListMsg, 0, sizeof(FileTransferMsg));
memset(&fileListInfo, 0, sizeof(FileListInfo));
/* fileListInfo can have null data if the folder is Empty
or if some error condition has occured.
The return value is 'failure' only if some error condition has occured.
*/
status = CreateFileListInfo(&fileListInfo, path, !(flags & 0x10));
if(status == FAILURE) {
fileListMsg = CreateFileListErrMsg(flags);
}
else {
/* DisplayFileList(fileListInfo); For Debugging */
fileListMsg = CreateFileListMsg(fileListInfo, flags);
FreeFileListInfo(fileListInfo);
}
return fileListMsg;
}
int
CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag)
{
DIR* pDir = NULL;
struct dirent* pDirent = NULL;
if((path == NULL) || (strlen(path) == 0)) {
/* In this case we will send the list of entries in ftp root*/
sprintf(path, "%s%s", GetFtpRoot(), "/");
}
if((pDir = opendir(path)) == NULL) {
rfbLog("File [%s]: Method [%s]: not able to open the dir\n",
__FILE__, __FUNCTION__);
return FAILURE;
}
while((pDirent = readdir(pDir))) {
if(strcmp(pDirent->d_name, ".") && strcmp(pDirent->d_name, "..")) {
struct stat stat_buf;
/*
int fpLen = sizeof(char)*(strlen(pDirent->d_name)+strlen(path)+2);
*/
char fullpath[PATH_MAX];
memset(fullpath, 0, PATH_MAX);
strcpy(fullpath, path);
if(path[strlen(path)-1] != '/')
strcat(fullpath, "/");
strcat(fullpath, pDirent->d_name);
if(stat(fullpath, &stat_buf) < 0) {
rfbLog("File [%s]: Method [%s]: Reading stat for file %s failed\n",
__FILE__, __FUNCTION__, fullpath);
continue;
}
if(S_ISDIR(stat_buf.st_mode)) {
if(AddFileListItemInfo(pFileListInfo, pDirent->d_name, -1, 0) == 0) {
rfbLog("File [%s]: Method [%s]: Add directory %s in the"
" list failed\n", __FILE__, __FUNCTION__, fullpath);
continue;
}
}
else {
if(flag) {
if(AddFileListItemInfo(pFileListInfo, pDirent->d_name,
stat_buf.st_size,
stat_buf.st_mtime) == 0) {
rfbLog("File [%s]: Method [%s]: Add file %s in the "
"list failed\n", __FILE__, __FUNCTION__, fullpath);
continue;
}
}
}
}
}
if(closedir(pDir) < 0) {
rfbLog("File [%s]: Method [%s]: ERROR Couldn't close dir\n",
__FILE__, __FUNCTION__);
}
return SUCCESS;
}
FileTransferMsg
CreateFileListErrMsg(char flags)
{
FileTransferMsg fileListMsg;
rfbFileListDataMsg* pFLD = NULL;
char* data = NULL;
unsigned int length = 0;
memset(&fileListMsg, 0, sizeof(FileTransferMsg));
data = (char*) calloc(sizeof(rfbFileListDataMsg), sizeof(char));
if(data == NULL) {
return fileListMsg;
}
length = sizeof(rfbFileListDataMsg) * sizeof(char);
pFLD = (rfbFileListDataMsg*) data;
pFLD->type = rfbFileListData;
pFLD->numFiles = Swap16IfLE(0);
pFLD->dataSize = Swap16IfLE(0);
pFLD->compressedSize = Swap16IfLE(0);
pFLD->flags = flags | 0x80;
fileListMsg.data = data;
fileListMsg.length = length;
return fileListMsg;
}
FileTransferMsg
CreateFileListMsg(FileListInfo fileListInfo, char flags)
{
FileTransferMsg fileListMsg;
rfbFileListDataMsg* pFLD = NULL;
char *data = NULL, *pFileNames = NULL;
unsigned int length = 0, dsSize = 0, i = 0;
FileListItemSizePtr pFileListItemSize = NULL;
memset(&fileListMsg, 0, sizeof(FileTransferMsg));
dsSize = fileListInfo.numEntries * 8;
length = sz_rfbFileListDataMsg + dsSize +
GetSumOfFileNamesLength(fileListInfo) +
fileListInfo.numEntries;
data = (char*) calloc(length, sizeof(char));
if(data == NULL) {
return fileListMsg;
}
pFLD = (rfbFileListDataMsg*) data;
pFileListItemSize = (FileListItemSizePtr) &data[sz_rfbFileListDataMsg];
pFileNames = &data[sz_rfbFileListDataMsg + dsSize];
pFLD->type = rfbFileListData;
pFLD->flags = flags & 0xF0;
pFLD->numFiles = Swap16IfLE(fileListInfo.numEntries);
pFLD->dataSize = Swap16IfLE(GetSumOfFileNamesLength(fileListInfo) +
fileListInfo.numEntries);
pFLD->compressedSize = pFLD->dataSize;
for(i =0; i <fileListInfo.numEntries; i++) {
pFileListItemSize[i].size = Swap32IfLE(GetFileSizeAt(fileListInfo, i));
pFileListItemSize[i].data = Swap32IfLE(GetFileDataAt(fileListInfo, i));
strcpy(pFileNames, GetFileNameAt(fileListInfo, i));
if(i+1 < fileListInfo.numEntries)
pFileNames += strlen(pFileNames) + 1;
}
fileListMsg.data = data;
fileListMsg.length = length;
return fileListMsg;
}
/******************************************************************************
* Methods to handle File Download Request.
******************************************************************************/
FileTransferMsg CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen);
FileTransferMsg CreateFileDownloadZeroSizeDataMsg(unsigned long mTime);
FileTransferMsg CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile);
FileTransferMsg
GetFileDownLoadErrMsg()
{
FileTransferMsg fileDownloadErrMsg;
char reason[] = "An internal error on the server caused download failure";
int reasonLen = strlen(reason);
memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
fileDownloadErrMsg = CreateFileDownloadErrMsg(reason, reasonLen);
return fileDownloadErrMsg;
}
FileTransferMsg
GetFileDownloadReadDataErrMsg()
{
char reason[] = "Cannot open file, perhaps it is absent or is a directory";
int reasonLen = strlen(reason);
return CreateFileDownloadErrMsg(reason, reasonLen);
}
FileTransferMsg
GetFileDownloadLengthErrResponseMsg()
{
char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
int reasonLen = strlen(reason);
return CreateFileDownloadErrMsg(reason, reasonLen);
}
FileTransferMsg
GetFileDownloadResponseMsgInBlocks(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
//const unsigned int sz_rfbBlockSize = SZ_RFBBLOCKSIZE;
int numOfBytesRead = 0;
char pBuf[SZ_RFBBLOCKSIZE];
char* path = rtcp->rcft.rcfd.fName;
memset(pBuf, 0, SZ_RFBBLOCKSIZE);
if((rtcp->rcft.rcfd.downloadInProgress == FALSE) && (rtcp->rcft.rcfd.downloadFD == -1)) {
if((rtcp->rcft.rcfd.downloadFD = open(path, O_RDONLY)) == -1) {
rfbLog("File [%s]: Method [%s]: Error: Couldn't open file\n",
__FILE__, __FUNCTION__);
return GetFileDownloadReadDataErrMsg();
}
rtcp->rcft.rcfd.downloadInProgress = TRUE;
}
if((rtcp->rcft.rcfd.downloadInProgress == TRUE) && (rtcp->rcft.rcfd.downloadFD != -1)) {
if( (numOfBytesRead = read(rtcp->rcft.rcfd.downloadFD, pBuf, SZ_RFBBLOCKSIZE)) <= 0) {
close(rtcp->rcft.rcfd.downloadFD);
rtcp->rcft.rcfd.downloadFD = -1;
rtcp->rcft.rcfd.downloadInProgress = FALSE;
if(numOfBytesRead == 0) {
return CreateFileDownloadZeroSizeDataMsg(rtcp->rcft.rcfd.mTime);
}
return GetFileDownloadReadDataErrMsg();
}
return CreateFileDownloadBlockSizeDataMsg(numOfBytesRead, pBuf);
}
}
FileTransferMsg
ChkFileDownloadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
FileTransferMsg fileDownloadMsg;
struct stat stat_buf;
int sz_rfbFileSize = 0;
char* path = rtcp->rcft.rcfd.fName;
memset(&fileDownloadMsg, 0, sizeof(FileTransferMsg));
if( (path == NULL) || (strlen(path) == 0) ||
(stat(path, &stat_buf) < 0) || (!(S_ISREG(stat_buf.st_mode))) ) {
char reason[] = "Cannot open file, perhaps it is absent or is not a regular file";
int reasonLen = strlen(reason);
rfbLog("File [%s]: Method [%s]: Reading stat for path %s failed\n",
__FILE__, __FUNCTION__, path);
fileDownloadMsg = CreateFileDownloadErrMsg(reason, reasonLen);
}
else {
rtcp->rcft.rcfd.mTime = stat_buf.st_mtime;
sz_rfbFileSize = stat_buf.st_size;
if(sz_rfbFileSize <= 0) {
fileDownloadMsg = CreateFileDownloadZeroSizeDataMsg(stat_buf.st_mtime);
}
}
return fileDownloadMsg;
}
FileTransferMsg
CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen)
{
FileTransferMsg fileDownloadErrMsg;
int length = sz_rfbFileDownloadFailedMsg + reasonLen + 1;
rfbFileDownloadFailedMsg *pFDF = NULL;
char *pFollow = NULL;
char *pData = (char*) calloc(length, sizeof(char));
memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
if(pData == NULL) {
rfbLog("File [%s]: Method [%s]: pData is NULL\n",
__FILE__, __FUNCTION__);
return fileDownloadErrMsg;
}
pFDF = (rfbFileDownloadFailedMsg *) pData;
pFollow = &pData[sz_rfbFileDownloadFailedMsg];
pFDF->type = rfbFileDownloadFailed;
pFDF->reasonLen = Swap16IfLE(reasonLen);
memcpy(pFollow, reason, reasonLen);
fileDownloadErrMsg.data = pData;
fileDownloadErrMsg.length = length;
return fileDownloadErrMsg;
}
FileTransferMsg
CreateFileDownloadZeroSizeDataMsg(unsigned long mTime)
{
FileTransferMsg fileDownloadZeroSizeDataMsg;
int length = sz_rfbFileDownloadDataMsg + sizeof(int);
rfbFileDownloadDataMsg *pFDD = NULL;
char *pFollow = NULL;
char *pData = (char*) calloc(length, sizeof(char));
memset(&fileDownloadZeroSizeDataMsg, 0, sizeof(FileTransferMsg));
if(pData == NULL) {
rfbLog("File [%s]: Method [%s]: pData is NULL\n",
__FILE__, __FUNCTION__);
return fileDownloadZeroSizeDataMsg;
}
pFDD = (rfbFileDownloadDataMsg *) pData;
pFollow = &pData[sz_rfbFileDownloadDataMsg];
pFDD->type = rfbFileDownloadData;
pFDD->compressLevel = 0;
pFDD->compressedSize = Swap16IfLE(0);
pFDD->realSize = Swap16IfLE(0);
memcpy(pFollow, &mTime, sizeof(unsigned long));
fileDownloadZeroSizeDataMsg.data = pData;
fileDownloadZeroSizeDataMsg.length = length;
return fileDownloadZeroSizeDataMsg;
}
FileTransferMsg
CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile)
{
FileTransferMsg fileDownloadBlockSizeDataMsg;
int length = sz_rfbFileDownloadDataMsg + sizeFile;
rfbFileDownloadDataMsg *pFDD = NULL;
char *pFollow = NULL;
char *pData = (char*) calloc(length, sizeof(char));
memset(&fileDownloadBlockSizeDataMsg, 0, sizeof(FileTransferMsg));
if(NULL == pData) {
rfbLog("File [%s]: Method [%s]: pData is NULL\n",
__FILE__, __FUNCTION__);
return fileDownloadBlockSizeDataMsg;
}
pFDD = (rfbFileDownloadDataMsg *) pData;
pFollow = &pData[sz_rfbFileDownloadDataMsg];
pFDD->type = rfbFileDownloadData;
pFDD->compressLevel = 0;
pFDD->compressedSize = Swap16IfLE(sizeFile);
pFDD->realSize = Swap16IfLE(sizeFile);
memcpy(pFollow, pFile, sizeFile);
fileDownloadBlockSizeDataMsg.data = pData;
fileDownloadBlockSizeDataMsg.length = length;
return fileDownloadBlockSizeDataMsg;
}
/******************************************************************************
* Methods to handle file upload request
******************************************************************************/
FileTransferMsg CreateFileUploadErrMsg(char* reason, unsigned int reasonLen);
FileTransferMsg
GetFileUploadLengthErrResponseMsg()
{
char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
int reasonLen = strlen(reason);
return CreateFileUploadErrMsg(reason, reasonLen);
}
FileTransferMsg
ChkFileUploadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
FileTransferMsg fileUploadErrMsg;
memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
if( (rtcp->rcft.rcfu.fName == NULL) ||
(strlen(rtcp->rcft.rcfu.fName) == 0) ||
((rtcp->rcft.rcfu.uploadFD = creat(rtcp->rcft.rcfu.fName,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1)) {
char reason[] = "Could not create file";
int reasonLen = strlen(reason);
fileUploadErrMsg = CreateFileUploadErrMsg(reason, reasonLen);
}
else
rtcp->rcft.rcfu.uploadInProgress = TRUE;
return fileUploadErrMsg;
}
FileTransferMsg
GetFileUploadCompressedLevelErrMsg()
{
char reason[] = "Server does not support data compression on upload";
int reasonLen = strlen(reason);
return CreateFileUploadErrMsg(reason, reasonLen);
}
FileTransferMsg
ChkFileUploadWriteErr(rfbClientPtr cl, rfbTightClientPtr rtcp, char* pBuf)
{
FileTransferMsg ftm;
unsigned long numOfBytesWritten = 0;
memset(&ftm, 0, sizeof(FileTransferMsg));
numOfBytesWritten = write(rtcp->rcft.rcfu.uploadFD, pBuf, rtcp->rcft.rcfu.fSize);
if(numOfBytesWritten != rtcp->rcft.rcfu.fSize) {
char reason[] = "Error writing file data";
int reasonLen = strlen(reason);
ftm = CreateFileUploadErrMsg(reason, reasonLen);
CloseUndoneFileTransfer(cl, rtcp);
}
return ftm;
}
void
FileUpdateComplete(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
/* Here we are settimg the modification and access time of the file */
/* Windows code stes mod/access/creation time of the file */
struct utimbuf utb;
utb.actime = utb.modtime = rtcp->rcft.rcfu.mTime;
if(utime(rtcp->rcft.rcfu.fName, &utb) == -1) {
rfbLog("File [%s]: Method [%s]: Setting the modification/access"
" time for the file <%s> failed\n", __FILE__,
__FUNCTION__, rtcp->rcft.rcfu.fName);
}
if(rtcp->rcft.rcfu.uploadFD != -1) {
close(rtcp->rcft.rcfu.uploadFD);
rtcp->rcft.rcfu.uploadFD = -1;
rtcp->rcft.rcfu.uploadInProgress = FALSE;
}
}
FileTransferMsg
CreateFileUploadErrMsg(char* reason, unsigned int reasonLen)
{
FileTransferMsg fileUploadErrMsg;
int length = sz_rfbFileUploadCancelMsg + reasonLen;
rfbFileUploadCancelMsg *pFDF = NULL;
char *pFollow = NULL;
char *pData = (char*) calloc(length, sizeof(char));
memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
if(pData == NULL) {
rfbLog("File [%s]: Method [%s]: pData is NULL\n",
__FILE__, __FUNCTION__);
return fileUploadErrMsg;
}
pFDF = (rfbFileUploadCancelMsg *) pData;
pFollow = &pData[sz_rfbFileUploadCancelMsg];
pFDF->type = rfbFileUploadCancel;
pFDF->reasonLen = Swap16IfLE(reasonLen);
memcpy(pFollow, reason, reasonLen);
fileUploadErrMsg.data = pData;
fileUploadErrMsg.length = length;
return fileUploadErrMsg;
}
/******************************************************************************
* Method to cancel File Transfer operation.
******************************************************************************/
void
CloseUndoneFileTransfer(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
/* TODO :: File Upload case is not handled currently */
/* TODO :: In case of concurrency we need to use Critical Section */
if(cl == NULL)
return;
if(rtcp->rcft.rcfu.uploadInProgress == TRUE) {
rtcp->rcft.rcfu.uploadInProgress = FALSE;
if(rtcp->rcft.rcfu.uploadFD != -1) {
close(rtcp->rcft.rcfu.uploadFD);
rtcp->rcft.rcfu.uploadFD = -1;
}
if(unlink(rtcp->rcft.rcfu.fName) == -1) {
rfbLog("File [%s]: Method [%s]: Delete operation on file <%s> failed\n",
__FILE__, __FUNCTION__, rtcp->rcft.rcfu.fName);
}
memset(rtcp->rcft.rcfu.fName, 0 , PATH_MAX);
}
if(rtcp->rcft.rcfd.downloadInProgress == TRUE) {
rtcp->rcft.rcfd.downloadInProgress = FALSE;
if(rtcp->rcft.rcfd.downloadFD != -1) {
close(rtcp->rcft.rcfd.downloadFD);
rtcp->rcft.rcfd.downloadFD = -1;
}
memset(rtcp->rcft.rcfd.fName, 0 , PATH_MAX);
}
}
/******************************************************************************
* Method to handle create directory request.
******************************************************************************/
void
CreateDirectory(char* dirName)
{
if(dirName == NULL) return;
if(mkdir(dirName, 0700) == -1) {
rfbLog("File [%s]: Method [%s]: Create operation for directory <%s> failed\n",
__FILE__, __FUNCTION__, dirName);
}
}

@ -0,0 +1,54 @@
/*
* Copyright (c) 2005 Novell, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com
*
* Author : Rohit Kumar
* Email ID : rokumar@novell.com
* Date : 14th July 2005
*/
#ifndef FILE_TRANSFER_MSG_H
#define FILE_TRANSFER_MSG_H
typedef struct _FileTransferMsg {
char* data;
unsigned int length;
} FileTransferMsg;
FileTransferMsg GetFileListResponseMsg(char* path, char flag);
FileTransferMsg GetFileDownloadResponseMsg(char* path);
FileTransferMsg GetFileDownloadLengthErrResponseMsg();
FileTransferMsg GetFileDownLoadErrMsg();
FileTransferMsg GetFileDownloadResponseMsgInBlocks(rfbClientPtr cl, rfbTightClientPtr data);
FileTransferMsg ChkFileDownloadErr(rfbClientPtr cl, rfbTightClientPtr data);
FileTransferMsg GetFileUploadLengthErrResponseMsg();
FileTransferMsg GetFileUploadCompressedLevelErrMsg();
FileTransferMsg ChkFileUploadErr(rfbClientPtr cl, rfbTightClientPtr data);
FileTransferMsg ChkFileUploadWriteErr(rfbClientPtr cl, rfbTightClientPtr data, char* pBuf);
void CreateDirectory(char* dirName);
void FileUpdateComplete(rfbClientPtr cl, rfbTightClientPtr data);
void CloseUndoneFileTransfer(rfbClientPtr cl, rfbTightClientPtr data);
void FreeFileTransferMsg(FileTransferMsg ftm);
#endif

@ -0,0 +1,988 @@
/*
* Copyright (c) 2005 Novell, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com
*
* Author : Rohit Kumar
* Email ID : rokumar@novell.com
* Date : 14th July 2005
*/
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <limits.h>
#include <rfb/rfb.h>
#include "rfbtightproto.h"
#include "filetransfermsg.h"
#include "handlefiletransferrequest.h"
pthread_mutex_t fileDownloadMutex = PTHREAD_MUTEX_INITIALIZER;
rfbBool fileTransferEnabled = TRUE;
rfbBool fileTransferInitted = FALSE;
char ftproot[PATH_MAX];
/******************************************************************************
* File Transfer Init methods. These methods are called for initializating
* File Transfer and setting ftproot.
******************************************************************************/
void InitFileTransfer();
int SetFtpRoot(char* path);
char* GetHomeDir(uid_t uid);
void FreeHomeDir(char *homedir);
/*
* InitFileTransfer method is called before parsing the command-line options
* for Xvnc. This sets the ftproot to the Home dir of the user running the Xvnc
* server. In case of error ftproot is set to '\0' char.
*/
void
InitFileTransfer()
{
char* userHome = NULL;
uid_t uid = geteuid();
if(fileTransferInitted)
return;
memset(ftproot, 0, sizeof(ftproot));
userHome = GetHomeDir(uid);
if((userHome != NULL) && (strlen(userHome) != 0)) {
SetFtpRoot(userHome);
FreeHomeDir(userHome);
}
fileTransferEnabled = TRUE;
fileTransferInitted = TRUE;
}
/*
* This method is called from InitFileTransfer method and
* if the command line option for ftproot is provided.
*/
int
SetFtpRoot(char* path)
{
struct stat stat_buf;
DIR* dir = NULL;
if((path == NULL) || (strlen(path) == 0) || (strlen(path) > (PATH_MAX - 1))) {
rfbLog("File [%s]: Method [%s]: parameter passed is improper, ftproot"
" not changed\n", __FILE__, __FUNCTION__);
return FALSE;
}
if(stat(path, &stat_buf) < 0) {
rfbLog("File [%s]: Method [%s]: Reading stat for file %s failed\n",
__FILE__, __FUNCTION__, path);
return FALSE;
}
if(S_ISDIR(stat_buf.st_mode) == 0) {
rfbLog("File [%s]: Method [%s]: path specified is not a directory\n",
__FILE__, __FUNCTION__);
return FALSE;
}
if((dir = opendir(path)) == NULL) {
rfbLog("File [%s]: Method [%s]: Not able to open the directory\n",
__FILE__, __FUNCTION__);
return FALSE;
}
else {
closedir(dir);
dir = NULL;
}
memset(ftproot, 0, PATH_MAX);
if(path[strlen(path)-1] == '/') {
memcpy(ftproot, path, strlen(path)-1);
}
else
memcpy(ftproot, path, strlen(path));
return TRUE;
}
/*
* Get the home directory for the user name
* param: username - name of the user for whom the home directory is required.
* returns: returns the home directory for the user, or null in case the entry
* is not found or any error. The returned string must be freed by calling the
* freehomedir function.
*/
char*
GetHomeDir(uid_t uid)
{
struct passwd *pwEnt = NULL;
char *homedir = NULL;
pwEnt = getpwuid (uid);
if (pwEnt == NULL)
return NULL;
if(pwEnt->pw_dir != NULL) {
homedir = strdup (pwEnt->pw_dir);
}
return homedir;
}
/*
* Free the home directory allocated by a previous call to retrieve the home
* directory. param: homedir - the string returned by a previous call to
* retrieve home directory for a user.
*/
void
FreeHomeDir(char *homedir)
{
free (homedir);
}
/******************************************************************************
* General methods.
******************************************************************************/
/*
* When the console sends the File Transfer Request, it sends the file path with
* ftproot as "/". So on Agent, to get the absolute file path we need to prepend
* the ftproot to it.
*/
char*
ConvertPath(char* path)
{
char p[PATH_MAX];
memset(p, 0, PATH_MAX);
if( (path == NULL) ||
(strlen(path) == 0) ||
(strlen(path)+strlen(ftproot) > PATH_MAX - 1) ) {
rfbLog("File [%s]: Method [%s]: cannot create path for file transfer\n",
__FILE__, __FUNCTION__);
return NULL;
}
memcpy(p, path, strlen(path));
memset(path, 0, PATH_MAX);
sprintf(path, "%s%s", ftproot, p);
return path;
}
void
EnableFileTransfer(rfbBool enable)
{
fileTransferEnabled = enable;
}
rfbBool
IsFileTransferEnabled()
{
return fileTransferEnabled;
}
char*
GetFtpRoot()
{
return ftproot;
}
/******************************************************************************
* Methods to Handle File List Request.
******************************************************************************/
/*
* HandleFileListRequest method is called when the server receives
* FileListRequest. In case of success a file list is sent to the client.
* For File List Request there is no failure reason sent.So here in case of any
* "unexpected" error no information will be sent. As these conditions should