added libvnc/ with RealVNC compatibility fix
[presencevnc] / libvnc / libvncserver / tightvnc-filetransfer / filetransfermsg.c
diff --git a/libvnc/libvncserver/tightvnc-filetransfer/filetransfermsg.c b/libvnc/libvncserver/tightvnc-filetransfer/filetransfermsg.c
new file mode 100644 (file)
index 0000000..7dae2cd
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * 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;
+}
+
+#ifndef __GNUC__
+#define __FUNCTION__ "unknown"
+#endif
+
+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);
+       }
+       return GetFileDownLoadErrMsg();
+}
+
+
+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);
+       }
+}
+