1/*
2 * Copyright (c) 2005 Novell, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, contact Novell, Inc.
16 *
17 * To contact Novell about this file by physical or electronic mail,
18 * you may find current contact information at www.novell.com
19 *
20 * Author		: Rohit Kumar
21 * Email ID	: rokumar@novell.com
22 * Date		: 14th July 2005
23 */
24
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <fcntl.h>
30#include <dirent.h>
31#include <utime.h>
32#include <errno.h>
33#include <unistd.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36
37#include <rfb/rfb.h>
38#include "rfbtightproto.h"
39#include "filelistinfo.h"
40#include "filetransfermsg.h"
41#include "handlefiletransferrequest.h"
42
43#define SZ_RFBBLOCKSIZE 8192
44
45
46void
47FreeFileTransferMsg(FileTransferMsg ftm)
48{
49
50	if(ftm.data != NULL) {
51		free(ftm.data);
52		ftm.data = NULL;
53	}
54
55	ftm.length = 0;
56
57}
58
59
60/******************************************************************************
61 * Methods to handle file list request.
62 ******************************************************************************/
63
64int CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag);
65FileTransferMsg CreateFileListErrMsg(char flags);
66FileTransferMsg CreateFileListMsg(FileListInfo fileListInfo, char flags);
67
68
69/*
70 * This is the method called by HandleFileListRequest to get the file list
71 */
72
73FileTransferMsg
74GetFileListResponseMsg(char* path, char flags)
75{
76	FileTransferMsg fileListMsg;
77	FileListInfo fileListInfo;
78	int status = -1;
79
80	memset(&fileListMsg, 0, sizeof(FileTransferMsg));
81	memset(&fileListInfo, 0, sizeof(FileListInfo));
82
83
84	 /* fileListInfo can have null data if the folder is Empty
85    	or if some error condition has occured.
86    	The return value is 'failure' only if some error condition has occured.
87	 */
88	status = CreateFileListInfo(&fileListInfo, path, !(flags  & 0x10));
89
90	if(status == FAILURE) {
91		fileListMsg = CreateFileListErrMsg(flags);
92	}
93	else {
94		/* DisplayFileList(fileListInfo); For Debugging  */
95
96		fileListMsg = CreateFileListMsg(fileListInfo, flags);
97		FreeFileListInfo(fileListInfo);
98	}
99
100	return fileListMsg;
101}
102
103#ifndef __GNUC__
104#define __FUNCTION__ "unknown"
105#endif
106
107int
108CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag)
109{
110	DIR* pDir = NULL;
111	struct dirent* pDirent = NULL;
112
113	if((path == NULL) || (strlen(path) == 0)) {
114		/* In this case we will send the list of entries in ftp root*/
115		sprintf(path, "%s%s", GetFtpRoot(), "/");
116	}
117
118	if((pDir = opendir(path)) == NULL) {
119		rfbLog("File [%s]: Method [%s]: not able to open the dir\n",
120				__FILE__, __FUNCTION__);
121		return FAILURE;
122	}
123
124	while((pDirent = readdir(pDir))) {
125		if(strcmp(pDirent->d_name, ".") && strcmp(pDirent->d_name, "..")) {
126			struct stat stat_buf;
127			/*
128			int fpLen = sizeof(char)*(strlen(pDirent->d_name)+strlen(path)+2);
129			*/
130			char fullpath[PATH_MAX];
131
132			memset(fullpath, 0, PATH_MAX);
133
134			strcpy(fullpath, path);
135			if(path[strlen(path)-1] != '/')
136				strcat(fullpath, "/");
137			strcat(fullpath, pDirent->d_name);
138
139			if(stat(fullpath, &stat_buf) < 0) {
140				rfbLog("File [%s]: Method [%s]: Reading stat for file %s failed\n",
141						__FILE__, __FUNCTION__, fullpath);
142				continue;
143			}
144
145			if(S_ISDIR(stat_buf.st_mode)) {
146				if(AddFileListItemInfo(pFileListInfo, pDirent->d_name, -1, 0) == 0) {
147					rfbLog("File [%s]: Method [%s]: Add directory %s in the"
148							" list failed\n", __FILE__, __FUNCTION__, fullpath);
149					continue;
150				}
151			}
152			else {
153				if(flag) {
154					if(AddFileListItemInfo(pFileListInfo, pDirent->d_name,
155												stat_buf.st_size,
156												stat_buf.st_mtime) == 0) {
157						rfbLog("File [%s]: Method [%s]: Add file %s in the "
158								"list failed\n", __FILE__, __FUNCTION__, fullpath);
159						continue;
160					}
161				}
162			}
163		}
164	}
165	if(closedir(pDir) < 0) {
166	    rfbLog("File [%s]: Method [%s]: ERROR Couldn't close dir\n",
167	    	__FILE__, __FUNCTION__);
168	}
169
170	return SUCCESS;
171}
172
173
174FileTransferMsg
175CreateFileListErrMsg(char flags)
176{
177	FileTransferMsg fileListMsg;
178	rfbFileListDataMsg* pFLD = NULL;
179	char* data = NULL;
180	unsigned int length = 0;
181
182	memset(&fileListMsg, 0, sizeof(FileTransferMsg));
183
184	data = (char*) calloc(sizeof(rfbFileListDataMsg), sizeof(char));
185	if(data == NULL) {
186		return fileListMsg;
187	}
188	length = sizeof(rfbFileListDataMsg) * sizeof(char);
189	pFLD = (rfbFileListDataMsg*) data;
190
191	pFLD->type = rfbFileListData;
192	pFLD->numFiles = Swap16IfLE(0);
193	pFLD->dataSize = Swap16IfLE(0);
194	pFLD->compressedSize = Swap16IfLE(0);
195	pFLD->flags = flags | 0x80;
196
197	fileListMsg.data = data;
198	fileListMsg.length = length;
199
200	return fileListMsg;
201}
202
203
204FileTransferMsg
205CreateFileListMsg(FileListInfo fileListInfo, char flags)
206{
207	FileTransferMsg fileListMsg;
208	rfbFileListDataMsg* pFLD = NULL;
209	char *data = NULL, *pFileNames = NULL;
210	unsigned int length = 0, dsSize = 0, i = 0;
211	FileListItemSizePtr pFileListItemSize = NULL;
212
213	memset(&fileListMsg, 0, sizeof(FileTransferMsg));
214	dsSize = fileListInfo.numEntries * 8;
215	length = sz_rfbFileListDataMsg + dsSize +
216			GetSumOfFileNamesLength(fileListInfo) +
217			fileListInfo.numEntries;
218
219	data = (char*) calloc(length, sizeof(char));
220	if(data == NULL) {
221		return fileListMsg;
222	}
223	pFLD = (rfbFileListDataMsg*) data;
224	pFileListItemSize = (FileListItemSizePtr) &data[sz_rfbFileListDataMsg];
225	pFileNames = &data[sz_rfbFileListDataMsg + dsSize];
226
227	pFLD->type            = rfbFileListData;
228    pFLD->flags 		  = flags & 0xF0;
229    pFLD->numFiles 		  = Swap16IfLE(fileListInfo.numEntries);
230    pFLD->dataSize 		  = Swap16IfLE(GetSumOfFileNamesLength(fileListInfo) +
231    									fileListInfo.numEntries);
232    pFLD->compressedSize  = pFLD->dataSize;
233
234	for(i =0; i <fileListInfo.numEntries; i++) {
235		pFileListItemSize[i].size = Swap32IfLE(GetFileSizeAt(fileListInfo, i));
236		pFileListItemSize[i].data = Swap32IfLE(GetFileDataAt(fileListInfo, i));
237		strcpy(pFileNames, GetFileNameAt(fileListInfo, i));
238
239		if(i+1 < fileListInfo.numEntries)
240			pFileNames += strlen(pFileNames) + 1;
241	}
242
243	fileListMsg.data 	= data;
244	fileListMsg.length 	= length;
245
246	return fileListMsg;
247}
248
249
250/******************************************************************************
251 * Methods to handle File Download Request.
252 ******************************************************************************/
253
254FileTransferMsg CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen);
255FileTransferMsg CreateFileDownloadZeroSizeDataMsg(unsigned long mTime);
256FileTransferMsg CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile);
257
258FileTransferMsg
259GetFileDownLoadErrMsg()
260{
261	FileTransferMsg fileDownloadErrMsg;
262
263	char reason[] = "An internal error on the server caused download failure";
264	int reasonLen = strlen(reason);
265
266	memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
267
268	fileDownloadErrMsg = CreateFileDownloadErrMsg(reason, reasonLen);
269
270	return fileDownloadErrMsg;
271}
272
273
274FileTransferMsg
275GetFileDownloadReadDataErrMsg()
276{
277	char reason[] = "Cannot open file, perhaps it is absent or is a directory";
278	int reasonLen = strlen(reason);
279
280	return CreateFileDownloadErrMsg(reason, reasonLen);
281
282}
283
284
285FileTransferMsg
286GetFileDownloadLengthErrResponseMsg()
287{
288	char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
289	int reasonLen = strlen(reason);
290
291	return CreateFileDownloadErrMsg(reason, reasonLen);
292}
293
294
295FileTransferMsg
296GetFileDownloadResponseMsgInBlocks(rfbClientPtr cl, rfbTightClientPtr rtcp)
297{
298	/* const unsigned int sz_rfbBlockSize = SZ_RFBBLOCKSIZE; */
299    int numOfBytesRead = 0;
300	char pBuf[SZ_RFBBLOCKSIZE];
301	char* path = rtcp->rcft.rcfd.fName;
302
303	memset(pBuf, 0, SZ_RFBBLOCKSIZE);
304
305	if((rtcp->rcft.rcfd.downloadInProgress == FALSE) && (rtcp->rcft.rcfd.downloadFD == -1)) {
306		if((rtcp->rcft.rcfd.downloadFD = open(path, O_RDONLY)) == -1) {
307			rfbLog("File [%s]: Method [%s]: Error: Couldn't open file\n",
308					__FILE__, __FUNCTION__);
309			return GetFileDownloadReadDataErrMsg();
310		}
311		rtcp->rcft.rcfd.downloadInProgress = TRUE;
312	}
313	if((rtcp->rcft.rcfd.downloadInProgress == TRUE) && (rtcp->rcft.rcfd.downloadFD != -1)) {
314		if( (numOfBytesRead = read(rtcp->rcft.rcfd.downloadFD, pBuf, SZ_RFBBLOCKSIZE)) <= 0) {
315			close(rtcp->rcft.rcfd.downloadFD);
316			rtcp->rcft.rcfd.downloadFD = -1;
317			rtcp->rcft.rcfd.downloadInProgress = FALSE;
318			if(numOfBytesRead == 0) {
319				return CreateFileDownloadZeroSizeDataMsg(rtcp->rcft.rcfd.mTime);
320			}
321			return GetFileDownloadReadDataErrMsg();
322		}
323	return CreateFileDownloadBlockSizeDataMsg(numOfBytesRead, pBuf);
324	}
325	return GetFileDownLoadErrMsg();
326}
327
328
329FileTransferMsg
330ChkFileDownloadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
331{
332    FileTransferMsg fileDownloadMsg;
333	struct stat stat_buf;
334	int sz_rfbFileSize = 0;
335	char* path = rtcp->rcft.rcfd.fName;
336
337	memset(&fileDownloadMsg, 0, sizeof(FileTransferMsg));
338
339	if( (path == NULL) || (strlen(path) == 0) ||
340		(stat(path, &stat_buf) < 0) || (!(S_ISREG(stat_buf.st_mode))) ) {
341
342			char reason[] = "Cannot open file, perhaps it is absent or is not a regular file";
343			int reasonLen = strlen(reason);
344
345			rfbLog("File [%s]: Method [%s]: Reading stat for path %s failed\n",
346					__FILE__, __FUNCTION__, path);
347
348			fileDownloadMsg = CreateFileDownloadErrMsg(reason, reasonLen);
349	}
350	else {
351		rtcp->rcft.rcfd.mTime = stat_buf.st_mtime;
352		sz_rfbFileSize = stat_buf.st_size;
353		if(sz_rfbFileSize <= 0) {
354			fileDownloadMsg = CreateFileDownloadZeroSizeDataMsg(stat_buf.st_mtime);
355		}
356
357	}
358	return fileDownloadMsg;
359}
360
361
362FileTransferMsg
363CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen)
364{
365	FileTransferMsg fileDownloadErrMsg;
366	int length = sz_rfbFileDownloadFailedMsg + reasonLen + 1;
367	rfbFileDownloadFailedMsg *pFDF = NULL;
368	char *pFollow = NULL;
369
370	char *pData = (char*) calloc(length, sizeof(char));
371	memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
372	if(pData == NULL) {
373		rfbLog("File [%s]: Method [%s]: pData is NULL\n",
374				__FILE__, __FUNCTION__);
375		return fileDownloadErrMsg;
376	}
377
378	pFDF = (rfbFileDownloadFailedMsg *) pData;
379	pFollow = &pData[sz_rfbFileDownloadFailedMsg];
380
381	pFDF->type = rfbFileDownloadFailed;
382	pFDF->reasonLen = Swap16IfLE(reasonLen);
383	memcpy(pFollow, reason, reasonLen);
384
385	fileDownloadErrMsg.data	= pData;
386	fileDownloadErrMsg.length	= length;
387
388	return fileDownloadErrMsg;
389}
390
391
392FileTransferMsg
393CreateFileDownloadZeroSizeDataMsg(unsigned long mTime)
394{
395	FileTransferMsg fileDownloadZeroSizeDataMsg;
396	int length = sz_rfbFileDownloadDataMsg + sizeof(unsigned long);
397	rfbFileDownloadDataMsg *pFDD = NULL;
398	char *pFollow = NULL;
399
400	char *pData = (char*) calloc(length, sizeof(char));
401	memset(&fileDownloadZeroSizeDataMsg, 0, sizeof(FileTransferMsg));
402	if(pData == NULL) {
403		rfbLog("File [%s]: Method [%s]: pData is NULL\n",
404				__FILE__, __FUNCTION__);
405		return fileDownloadZeroSizeDataMsg;
406	}
407
408	pFDD = (rfbFileDownloadDataMsg *) pData;
409	pFollow = &pData[sz_rfbFileDownloadDataMsg];
410
411	pFDD->type = rfbFileDownloadData;
412	pFDD->compressLevel = 0;
413	pFDD->compressedSize = Swap16IfLE(0);
414	pFDD->realSize = Swap16IfLE(0);
415
416	memcpy(pFollow, &mTime, sizeof(unsigned long));
417
418	fileDownloadZeroSizeDataMsg.data	= pData;
419	fileDownloadZeroSizeDataMsg.length	= length;
420
421	return fileDownloadZeroSizeDataMsg;
422
423}
424
425
426FileTransferMsg
427CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile)
428{
429	FileTransferMsg fileDownloadBlockSizeDataMsg;
430	int length = sz_rfbFileDownloadDataMsg + sizeFile;
431	rfbFileDownloadDataMsg *pFDD = NULL;
432	char *pFollow = NULL;
433
434	char *pData = (char*) calloc(length, sizeof(char));
435	memset(&fileDownloadBlockSizeDataMsg, 0, sizeof(FileTransferMsg));
436	if(NULL == pData) {
437		rfbLog("File [%s]: Method [%s]: pData is NULL\n",
438				__FILE__, __FUNCTION__);
439		return fileDownloadBlockSizeDataMsg;
440	}
441
442	pFDD = (rfbFileDownloadDataMsg *) pData;
443	pFollow = &pData[sz_rfbFileDownloadDataMsg];
444
445	pFDD->type = rfbFileDownloadData;
446	pFDD->compressLevel = 0;
447	pFDD->compressedSize = Swap16IfLE(sizeFile);
448	pFDD->realSize = Swap16IfLE(sizeFile);
449
450	memcpy(pFollow, pFile, sizeFile);
451
452	fileDownloadBlockSizeDataMsg.data	= pData;
453	fileDownloadBlockSizeDataMsg.length	= length;
454
455	return fileDownloadBlockSizeDataMsg;
456
457}
458
459
460/******************************************************************************
461 * Methods to handle file upload request
462 ******************************************************************************/
463
464FileTransferMsg CreateFileUploadErrMsg(char* reason, unsigned int reasonLen);
465
466FileTransferMsg
467GetFileUploadLengthErrResponseMsg()
468{
469	char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
470	int reasonLen = strlen(reason);
471
472	return CreateFileUploadErrMsg(reason, reasonLen);
473}
474
475
476FileTransferMsg
477ChkFileUploadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
478{
479    FileTransferMsg fileUploadErrMsg;
480
481	memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
482	if( (rtcp->rcft.rcfu.fName == NULL) ||
483		(strlen(rtcp->rcft.rcfu.fName) == 0) ||
484		((rtcp->rcft.rcfu.uploadFD = creat(rtcp->rcft.rcfu.fName,
485		S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1)) {
486
487			char reason[] = "Could not create file";
488			int reasonLen = strlen(reason);
489			fileUploadErrMsg = CreateFileUploadErrMsg(reason, reasonLen);
490	}
491	else
492		rtcp->rcft.rcfu.uploadInProgress = TRUE;
493
494	return fileUploadErrMsg;
495}
496
497
498FileTransferMsg
499GetFileUploadCompressedLevelErrMsg()
500{
501	char reason[] = "Server does not support data compression on upload";
502	int reasonLen = strlen(reason);
503
504	return CreateFileUploadErrMsg(reason, reasonLen);
505}
506
507
508FileTransferMsg
509ChkFileUploadWriteErr(rfbClientPtr cl, rfbTightClientPtr rtcp, char* pBuf)
510{
511	FileTransferMsg ftm;
512	unsigned long numOfBytesWritten = 0;
513
514	memset(&ftm, 0, sizeof(FileTransferMsg));
515
516	numOfBytesWritten = write(rtcp->rcft.rcfu.uploadFD, pBuf, rtcp->rcft.rcfu.fSize);
517
518	if(numOfBytesWritten != rtcp->rcft.rcfu.fSize) {
519		char reason[] = "Error writing file data";
520		int reasonLen = strlen(reason);
521		ftm = CreateFileUploadErrMsg(reason, reasonLen);
522		CloseUndoneFileTransfer(cl, rtcp);
523	}
524	return ftm;
525}
526
527
528void
529FileUpdateComplete(rfbClientPtr cl, rfbTightClientPtr rtcp)
530{
531	/* Here we are settimg the modification and access time of the file */
532	/* Windows code stes mod/access/creation time of the file */
533	struct utimbuf utb;
534
535	utb.actime = utb.modtime = rtcp->rcft.rcfu.mTime;
536	if(utime(rtcp->rcft.rcfu.fName, &utb) == -1) {
537		rfbLog("File [%s]: Method [%s]: Setting the modification/access"
538				" time for the file <%s> failed\n", __FILE__,
539				__FUNCTION__, rtcp->rcft.rcfu.fName);
540	}
541
542	if(rtcp->rcft.rcfu.uploadFD != -1) {
543		close(rtcp->rcft.rcfu.uploadFD);
544		rtcp->rcft.rcfu.uploadFD = -1;
545		rtcp->rcft.rcfu.uploadInProgress = FALSE;
546	}
547}
548
549
550FileTransferMsg
551CreateFileUploadErrMsg(char* reason, unsigned int reasonLen)
552{
553	FileTransferMsg fileUploadErrMsg;
554	int length = sz_rfbFileUploadCancelMsg + reasonLen;
555	rfbFileUploadCancelMsg *pFDF = NULL;
556	char *pFollow = NULL;
557
558	char *pData = (char*) calloc(length, sizeof(char));
559	memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
560	if(pData == NULL) {
561		rfbLog("File [%s]: Method [%s]: pData is NULL\n",
562				__FILE__, __FUNCTION__);
563		return fileUploadErrMsg;
564	}
565
566	pFDF = (rfbFileUploadCancelMsg *) pData;
567	pFollow = &pData[sz_rfbFileUploadCancelMsg];
568
569	pFDF->type = rfbFileUploadCancel;
570	pFDF->reasonLen = Swap16IfLE(reasonLen);
571	memcpy(pFollow, reason, reasonLen);
572
573	fileUploadErrMsg.data		= pData;
574	fileUploadErrMsg.length		= length;
575
576	return fileUploadErrMsg;
577}
578
579
580/******************************************************************************
581 * Method to cancel File Transfer operation.
582 ******************************************************************************/
583
584void
585CloseUndoneFileTransfer(rfbClientPtr cl, rfbTightClientPtr rtcp)
586{
587	/* TODO :: File Upload case is not handled currently */
588	/* TODO :: In case of concurrency we need to use Critical Section */
589
590	if(cl == NULL)
591		return;
592
593
594	if(rtcp->rcft.rcfu.uploadInProgress == TRUE) {
595		rtcp->rcft.rcfu.uploadInProgress = FALSE;
596
597		if(rtcp->rcft.rcfu.uploadFD != -1) {
598			close(rtcp->rcft.rcfu.uploadFD);
599			rtcp->rcft.rcfu.uploadFD = -1;
600		}
601
602		if(unlink(rtcp->rcft.rcfu.fName) == -1) {
603			rfbLog("File [%s]: Method [%s]: Delete operation on file <%s> failed\n",
604					__FILE__, __FUNCTION__, rtcp->rcft.rcfu.fName);
605		}
606
607		memset(rtcp->rcft.rcfu.fName, 0 , PATH_MAX);
608	}
609
610	if(rtcp->rcft.rcfd.downloadInProgress == TRUE) {
611		rtcp->rcft.rcfd.downloadInProgress = FALSE;
612
613		if(rtcp->rcft.rcfd.downloadFD != -1) {
614			close(rtcp->rcft.rcfd.downloadFD);
615			rtcp->rcft.rcfd.downloadFD = -1;
616		}
617		memset(rtcp->rcft.rcfd.fName, 0 , PATH_MAX);
618	}
619}
620
621
622/******************************************************************************
623 * Method to handle create directory request.
624 ******************************************************************************/
625
626void
627CreateDirectory(char* dirName)
628{
629	if(dirName == NULL) return;
630
631	if(mkdir(dirName, 0700) == -1) {
632		rfbLog("File [%s]: Method [%s]: Create operation for directory <%s> failed\n",
633				__FILE__, __FUNCTION__, dirName);
634	}
635}
636
637