/******************************************************************************
 *
 * Copyright (c) 1999	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	Solftp.cpp
 *
 *****************************************************************************/

// SOL++2000
// 2000.02.18


#include "solftp.h"
#include "appconst.h"
#include "resource.h"
#include <sol\clientdc.h>
#include <sol\file.h>
#include <sol\stdio.h>
#include <direct.h>

#define XPrintf Printf


SolFtp::SolFtp(Application& applet, const char* name, Args& args)
	:ApplicationView(applet, name, args)
{
	connected = FALSE;

	HINSTANCE hInst = getInstanceHandle();
	int fontSize = FONT_LSIZE;
	
	if(::GetSystemMetrics(SM_CXSCREEN) <= DISPLAY_WIDTH &&
		::GetSystemMetrics(SM_CYSCREEN) <= DISPLAY_HEIGHT) {
		fontSize = FONT_SSIZE;
	}

	ClientDC dc(this);
	UINT charSet = dc.getTextCharsetInfo(NULL);

	Args ar;
	ar.reset();
	ar.set(XmNheight, fontSize);
	ar.set(XmNcharSet, (ulong)charSet);

	font = new Font(ar);

	ar.reset();
	text = new ScrolledRichText(this, "", ar);

	ar.reset();
	binary = new RadioButton(this, "Binary", ar);
	binary -> setFont(font);
	binary -> disable();

	ar.reset();
	ascii = new RadioButton(this, "Text", ar);
	ascii -> setFont(font);
	ascii -> setCheck(TRUE);
	ascii -> disable();

	ar.reset();
	ar.set(XmNstyle, (ulong)SS_SUNKEN);
	localLabel = new Static(this, "Local", ar);
	localLabel -> setFont(font);

	ar.reset();
	ar.set(XmNstyle, (ulong)SS_SUNKEN);
	remoteLabel = new Static(this, "Remote", ar);
	remoteLabel -> setFont(font);
	
	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE);
	ar.set(XmNstyle, (ulong)WS_BORDER|TVS_HASBUTTONS|\
					 TVS_HASLINES|TVS_LINESATROOT|TVS_SHOWSELALWAYS);
	remoteTree = new DirTreeView(this, "", ar);
	
	remoteTree -> addCallback(XmNdoubleClickCallback, this,
			(Callback)&SolFtp::remoteNodeClicked, NULL);


	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE);
	ar.set(XmNstyle, 
			(ulong)WS_BORDER|WS_VSCROLL|WS_HSCROLL|
				LVS_LIST|LVS_SORTDESCENDING|LVS_SHOWSELALWAYS);

	remoteFile = new FileListView(this, "", ar);
	remoteFile -> addCallback(XmNbeginDragCallback, this,
		(Callback)&SolFtp::remoteFileDrag, NULL);


	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE);
	ar.set(XmNstyle, 
			(ulong)	WS_BORDER|WS_VSCROLL|WS_HSCROLL|
				LVS_LIST|LVS_SORTDESCENDING|LVS_SHOWSELALWAYS);
	
	localFile = new FileListView(this, "", ar);

	localFile -> addCallback(XmNbeginDragCallback, this,
		(Callback)&SolFtp::localFileDrag, NULL);

	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE);
	ar.set(XmNstyle, (ulong)WS_BORDER|TVS_HASBUTTONS|\
					 TVS_HASLINES|TVS_LINESATROOT|TVS_SHOWSELALWAYS);

	localTree = new DirTreeView(this, "", ar);
	localTree -> addCallback(XmNdoubleClickCallback, this,
		(Callback)&SolFtp::localNodeClicked, NULL);

	localTree -> addCallback(XmNitemExpandingCallback, this,
		(Callback)&SolFtp::localTreeExpanding, NULL);

	dirImageList = new ImageList(16, 16, ILC_COLOR, 2,1);
	dirImageList -> setBkColor(::GetSysColor(COLOR_WINDOW));

	iconId1 = dirImageList -> addIcon(
				LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1) ));
	iconId2 = dirImageList -> addIcon(
				LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON2)));
	localTree  -> setImageList(dirImageList, 0);
	remoteTree -> setImageList(dirImageList, 0);

	fileImageList  = new ImageList(16, 16, ILC_MASK, 0, 5);

	localFile  -> setImageList(fileImageList, LVSIL_SMALL);

	remoteFile -> setImageList(fileImageList, LVSIL_SMALL);

	fileImageId = fileImageList -> addIcon(
				LoadIcon(hInst, MAKEINTRESOURCE(IDI_FILE) ));


	ar.reset();
	loginDlg = new LoginDialog(this, "", ar);

	addEventHandler(WM_LBUTTONUP, this,
		(Handler)&SolFtp::lButtonUp, NULL);
	addEventHandler(WM_MOUSEMOVE, this,
		(Handler)&SolFtp::mouseMove, NULL);

	addEventHandler(WM_FILESLIST, this,
		(Handler)&SolFtp::listUpLocalFile, NULL);

	addEventHandler(WM_STARTSESSION, this,
		(Handler)&SolFtp::start, NULL);

	addEventHandler(WM_ASYNC_SELECT, this,
		(Handler)&SolFtp::select, NULL);

	dirPopupMenu = new PopupMenu(this);

	dirPopupMenu ->append(IDM_MKDIR, "mkdir");
	dirPopupMenu ->append(IDM_RENAME, "rename");
	dirPopupMenu ->append(IDM_RMDIR, "rmdir");
	localTree -> setPopupMenu(dirPopupMenu);
	remoteTree -> setPopupMenu(dirPopupMenu);

	addCallback(XmNmenuCallback, IDM_CONNECT, this,
		(Callback)&SolFtp::login, NULL);		

	addCallback(XmNmenuCallback, IDM_DISCONNECT, this,
		(Callback)&SolFtp::disConnect, NULL);		
	addCallback(XmNmenuCallback, IDM_EXIT, this,
		(Callback)&SolFtp::exit, NULL);		

	addCallback(XmNmenuCallback, IDM_MKDIR, this,
		(Callback)&SolFtp::handleDirPopupMenu, NULL);		
	addCallback(XmNmenuCallback, IDM_RENAME, this,
		(Callback)&SolFtp::handleDirPopupMenu, NULL);		
	addCallback(XmNmenuCallback, IDM_RMDIR, this,
		(Callback)&SolFtp::handleDirPopupMenu, NULL);		

	controlChannel = NULL;
	dataChannel    = NULL;

	imgList = NULL;
	nextCommand = -1;
	dragging = FALSE;
	hcurrentRemoteItem = NULL;

	post(WM_FILESLIST, 0, 0);

}
		
SolFtp::~SolFtp()
{
	delete controlChannel;
	delete dataChannel;

	delete	loginDlg;
	delete	dirImageList;
	delete	fileImageList;
	delete	imgList;
	delete	text;
	delete  binary;
	delete  ascii;

	delete	localTree;
	delete	remoteTree;
	delete	localFile;
	delete	remoteFile;
	delete  font;
	delete dirPopupMenu;

}

void SolFtp::handleDirPopupMenu(Action& action)
{
	Event& event = action.getEvent();
	int menuId = event.getMenuId();
	Printf("handleDirPopupMenu %d\r\n", menuId);

}


void SolFtp::localNodeClicked(Action& action)
{
	char* dir = localTree -> doubleClick(action);
	if(dir) {
		localFile -> deleteAllItems();
		localFile -> findFiles(dir);	
		localLabel-> setText(dir);
	}
}


void SolFtp::localTreeExpanding(Action& action)
{							 
	Event& event = action.getEvent();	
	NM_TREEVIEW* nmtreev = (NM_TREEVIEW*)event.getLParam();
	HTREEITEM htreeItem = nmtreev->itemNew.hItem;
	char dir[_MAX_PATH];
	dir[0] = NULL;
	// 選択されたノードのディレクトリのフルパス名を得る
	localTree -> getHierachy(htreeItem, dir, "\\");

	// 子どもをすべて削除する
	localTree -> deleteChildren(htreeItem);
	// サブディレクトリのツリーを作る
	localTree -> findDirectories(htreeItem, dir, 0, 2);
	// -> SortItems(htreeItem, TRUE);
	localTree -> sortChildren(htreeItem, TRUE);

}


void SolFtp::remoteNodeClicked(Action& action)
{
	XPrintf("SoltFtp::RemoteNodeClicked \r\n");

	char* dir = remoteTree -> doubleClick(action);

	HTREEITEM htreeItem = remoteTree -> getCurrentNode();

//	BOOL readable = (BOOL) (treev->itemOld.lParam);
	BOOL readable = TRUE;
	if(htreeItem && readable) {
			char dir[_MAX_PATH];
			dir[0] = '/';
			dir[1] = NULL;
			remoteTree -> getHierachy(htreeItem, &dir[1], "/");

			XPrintf("Remote Dir %s\n", dir);
			char command[256];
			sprintf(command, "CWD %s\r\n", dir);
            hcurrentRemoteItem = htreeItem;

			XPrintf("SolFtp::RemoteNodeClicked\r\n");

			remoteTree -> deleteChildren(htreeItem);
			nextCommand = CWD;

			sendCommand(command);

			remoteFile -> deleteAllItems();
			//nextCommand = LIST;

			listupRemoteFiles();
		} //readable
	else {
		MessageBox(NULL, "Permission denied", "FTP", MB_OK);
	}	
}


long SolFtp::listUpLocalFile(Event& event)
{
	char dir[_MAX_PATH];
	::GetWindowsDirectory(dir, sizeof(dir));
	char* p = strchr(dir, ':');
	if(p++) {
		*p = Zero;
	}

	char path[10];
	DWORD d = ::GetLogicalDrives();
	for(int i = 0; i<26; i++) {
		if(d & 1) {
			sprintf(path, "%c:", 'A'+i);
			HTREEITEM x = localTree -> addItem(NULL, TVI_LAST, path);
			localTree -> findDirectories(x, dir, 0, 2);
			localTree -> sortChildren(x, TRUE);
			if(path[0] == dir[0]) {
				localTree -> expand(x, TVE_EXPAND);
			}
		}
		d = d >> 1;
	}
	return 0L;
}


void SolFtp::listupRemoteFiles()
{
	if(controlChannel == NULL) {
		return;
	}

	listenSocket = NULL;
	if ((listenSocket = createListenSocket())
			 	== NULL) {
		return;
	}
	listupRemoteFiles2();
}


void SolFtp::listupRemoteFiles2()
{
	sendCommand("LIST\r\n");
	if (acceptDataConnection()== FALSE) {
		return;
	}

	char dir[_MAX_PATH];

	char filename[_MAX_PATH];
	filename[0] = NULL;
	
	::GetWindowsDirectory(dir, sizeof(dir));
	::GetTempFileName(dir, "FTP", 1, filename);

	readData(filename);

	HTREEITEM xtemp = remoteTree -> getCurrentNode();
	char tree[300];
	tree[0] = Zero;
	remoteTree -> getHierachy(xtemp, tree, "/");
	XPrintf("SolFtp::ListupRemoteFiles2 %s\r\n", tree);
	remoteLabel -> setText(tree);
	setSysCursor(IDC_WAIT);

	FILE* fp = fopen(filename, "r");
	if(fp) {
		static char line[1024];
		HTREEITEM item = remoteTree->getLeaf();
		if(hcurrentRemoteItem) {
			item = hcurrentRemoteItem;
		}

		tree[0] = Zero;

		remoteTree -> getHierachy(item, tree, "/");
		while(fgets(line, sizeof(line), fp) !=NULL) {
			int len = strlen(line);
			if(len >= 2 /*&& line[len-2] == '\r' && line[len-1] == '\r\n'*/) {
				line[len-1] = Zero;
			}
		
			if(line[0] == 'd' || line[0] == '-' || line[0] == 'l') {
				char* name = strrchr(line, ' ');
				if(name) {
					name++;
					if(line[0] == 'd') {
						BOOL readable = FALSE;
						if(line[1] == 'r') {
							readable = TRUE;
						}
						XPrintf("RemoteTree addItem %s\r\n", name);
						// 1999.06.12 < 
						if (strcmp(name, ".") != 0 && strcmp(name, "..") !=0) {
							remoteTree->addItemWithFlag(item, TVI_LAST, name, readable);
						}
					}
					else {
						LV_ITEM item;
						memset(&item, 0, sizeof(LV_ITEM));
						item.mask = LVIF_TEXT|LVIF_IMAGE;
						item.pszText    = name;
						item.cchTextMax = 255;
					    item.iImage = fileImageId;
						remoteFile -> insertItem(&item);
					}
				}
			}
		}
		fclose(fp);
	}
    unlink(filename);
	
	dataChannel -> close();
	delete dataChannel;
	dataChannel = NULL;

	remoteTree -> sortChildren(xtemp, TRUE);
	setSysCursor(IDC_ARROW);

	nextCommand = -1;

	return;
} 


void SolFtp::login(Action& action) 
{
	loginDlg -> popup(action);
}


long SolFtp::start(Event& event)
{
	if(controlChannel) {
		delete controlChannel;
	}
	controlChannel = new SocketStream(PF_INET, IPPROTO_TCP);

	sockaddr_in addr;				
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;		

	char* host = loginDlg -> getHostName();

    // numeric format？
	unsigned long inetAddr = inet_addr(host);
	if(inetAddr != INADDR_NONE) {
		addr.sin_addr.s_addr = inetAddr;
	}

	if(inetAddr == INADDR_NONE) {
		// name format?
		LPHOSTENT hostEnt = gethostbyname(host);
		if(hostEnt == NULL) {
			MessageBox(NULL, "Error:Resolving address", "SOLFTP", MB_OK);
			XPrintf("SolFtp::Connect Error:resolving address\r\n");
			return 0L;
		}
		addr.sin_addr = *((LPIN_ADDR)*hostEnt->h_addr_list);
	}

    SERVENT* service = getservbyname("ftp", 0);
	if (service) {
		addr.sin_port = service->s_port;
	}
	else {
		addr.sin_port = htons(IPPORT_FTP);
	}
	controlChannel -> select (getWindow(), WM_ASYNC_SELECT, FD_CONNECT);  

	controlChannel -> connect((sockaddr*)&addr, sizeof(addr));
	XPrintf("SolFtp::Connect\r\n");

	return 0L;
}



long SolFtp::select(Event& event)
{
	XPrintf("SolFtp::Select\r\n");

	LPARAM lParam = event.getLParam();

	switch(WSAGETSELECTEVENT(lParam)) {

	case FD_CONNECT: 
		if(controlChannel) {
			XPrintf("FD_CONNECT: Connected\n");
			controlChannel -> select(getWindow(), WM_ASYNC_SELECT, 
				   FD_CLOSE| FD_READ| FD_WRITE);  
			connected = TRUE;
		}						 
		break;

	case FD_READ: 
		readReply();
		break;

	case FD_CLOSE:
		showMessageDialog("SOLFTP", "Conntrol connection is closed", MB_OK);
		connected = FALSE;
		break;

	default:
		break;
	}
	return 0L;
}


void SolFtp::interpretCommand(char* buffer)
{
	int len = strlen(buffer);
	if(buffer[len] == '\n') {
		buffer[len] = Zero;
	}

	XPrintf("SolFtp::InterpretCommand <%s>\r\n", buffer);
	char code[10];
	strncpy(code, buffer, 3);
	code[3] = Zero;
	int reply = atoi(code);
	char* message = buffer + 4;

	// Error
	if(reply >= 500 && reply <600) {
		MessageBox(NULL, message, "SOLFTP", MB_OK);			
		return;
	}

	switch(reply) {

	case 220:
		{
			char command[128];
			char* userName = loginDlg -> getUserName();
			char* password = loginDlg -> getPassword();
	
			sprintf(command, "USER %s\r\n", userName);
			sendCommand(command);

			sprintf(command, "PASS %s\r\n", password);
			sendCommand(command);
		}
		break;

	case 230:
		{
		// logged in
			binary -> enable();
			ascii  -> enable();
			sendCommand("PWD\n");   
        }
		break;

	case 200:
		if(message && strncmp(message, "PORT", 4) == 0) {
			;
		}
		break;

	case 257:
		{
			char* dir = buffer;
			char* x= strrchr(buffer, '"');
			if(x) {
				*x = Zero;
			}
			dir += 5;
			remoteTree -> buildHierachy(TVI_ROOT, ++dir);
			listupRemoteFiles();
			remoteTree -> expand(remoteTree->getRoot(), TVE_EXPAND);
			remoteTree -> expandChildren(remoteTree->getRoot(), TVE_EXPAND);
		}
		break;

	default:
		break;
	}
}



int SolFtp::sendCommand(char* command)
{
	if(controlChannel == NULL) {
		return FALSE;
	}

	if ((controlChannel->send(command, strlen(command), 0)) == SOCKET_ERROR) {
		return 999;
	} 

	if(strncmp(command, "PORT", 4) == 0) {
		state = PORT_REPLY_WAITING;
	}
	return TRUE; 
}	


int SolFtp::readReply()
{		
	if(controlChannel == NULL) {
		return FALSE;
	}


	int bytesRead =0;
	int end = 0;
	int bufferLength = sizeof(replyBuffer);
	int remaining = bufferLength;
	do {
		remaining -= end;
		bytesRead = controlChannel -> recv(replyBuffer+end, 
							remaining, 0);    			
    	end += bytesRead;
	} while (bytesRead > 0 && end < bufferLength); 

	replyBuffer[end] = Zero;

	char* px = replyBuffer;

	while(px) {
		char* tail = strstr(px, "\r\n");
		if(tail) {
			*tail = NULL;
		}

		interpretCommand(px);

		if(tail) {
			px = tail +2;
		}
		if(tail == NULL) {
			break;
		}
	}
	return TRUE;
}	


BOOL SolFtp::requestDataConnection(SocketStream* listenSocket)	
{	
	XPrintf("SolFtp::RequestDataConnection \r\n");
	sockaddr_in addr;

	if (listenSocket -> getName(&addr) == SOCKET_ERROR) {
		return FALSE;
	}

	int localPort = addr.sin_port;
								
	if (controlChannel -> getName(&addr) == SOCKET_ERROR) {
		return FALSE;
	}

	char command[128];					
	wsprintf(command, "PORT %d,%d,%d,%d,%d,%d\r\n", 
					addr.sin_addr.S_un.S_un_b.s_b1, 
					addr.sin_addr.S_un.S_un_b.s_b2,
					addr.sin_addr.S_un.S_un_b.s_b3,
					addr.sin_addr.S_un.S_un_b.s_b4,
					localPort & 0xFF,	
					localPort >> 8);

	sendCommand(command);

	return TRUE;
}

SocketStream* SolFtp::createListenSocket()	
{
	XPrintf("SolFtp::createListenSocket\r\n");

	listenSocket = new SocketStream(PF_INET,IPPROTO_TCP);

	if (listenSocket == NULL) {
		return NULL;
	}

	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;		
	addr.sin_port   = htons(0);
	addr.sin_addr.s_addr = INADDR_ANY;
						
		
	if (listenSocket -> bind((sockaddr*)&addr, sizeof(addr))!= 0) {
		delete listenSocket;
		listenSocket = NULL;
		return NULL;
	}

	if (listenSocket -> listen(5) !=0) {
		delete listenSocket;
		listenSocket = NULL;
		return NULL;
	}
		
	if(requestDataConnection(listenSocket) == FALSE){
		delete listenSocket;
		listenSocket = NULL;
		return NULL;
	}
	return listenSocket;	
}	

		
BOOL SolFtp::acceptDataConnection()	
{
	if(listenSocket == NULL) {
		return FALSE;
	}

	sockaddr_in addr;	
	int length = sizeof(addr);	
	dataChannel = listenSocket -> accept((sockaddr*)&addr, &length);

	delete listenSocket;
	listenSocket = NULL;				
		
	if (dataChannel == NULL) {
		XPrintf("SolFtp::AcceptDataConnection\r\n");
		return FALSE;
	}
	return TRUE;
}	


BOOL SolFtp::readData(char* fileName)
{
	XPrintf("SolFtp::ReadData\r\n");

	if(dataChannel == NULL) {
		return FALSE;
	}

	File file;
	if(file.create(fileName) == FALSE) {
		return FALSE;
	}
			

	int bytesRecv;				
	int data = 0;			

    do {
    	bytesRecv = dataChannel -> recv(readBuffer, sizeof(readBuffer), 0);
    					
    	data += bytesRecv;
    	if (bytesRecv > 0 ) {
			if(file.write(readBuffer, bytesRecv) <= 0 ) {
	    		break;
    		}
		 }
	} while (bytesRecv > 0);					
	file.close();

	return TRUE;
}	


BOOL SolFtp::writeData(char* fileName)	
{
	XPrintf("SolFtp::WriteData %s\r\n", fileName);

	if(dataChannel == NULL) {
		return FALSE;
	}

	//static char dataBuffer[1000];
	int data = 0;			
	File file;
	if(file.openReadOnly(fileName) == FALSE) {
		XPrintf("SolFtp::WriteData OpenReadOnly Failed\r\n");
		return FALSE;
	}
	int readBytes = 0;
	
    while((readBytes = file.read(dataBuffer, sizeof(dataBuffer)) ) != 0) {
		if(readBytes == 0) {
			break;
		}
    	int nBytesSend = dataChannel -> send(dataBuffer, readBytes, 0);
	} 
								
	return	TRUE;
}	

void SolFtp::disConnect(Action& action)
{
	if(controlChannel) {
		if(MessageBox(NULL, "Do you terminate a current session?", 
			"SOLFTP", MB_OKCANCEL) == IDOK) {

			controlChannel -> shutdown(0);
			delete controlChannel;
			controlChannel = NULL;

			remoteFile -> deleteAllItems();
			remoteTree -> deleteAllItems();
			hcurrentRemoteItem = NULL;
			connected = FALSE;

		}
	}
}


// in localfile
void SolFtp::localFileDrag(Action& action)
{
	if(connected == FALSE) {
		return;
	}


	Event& event = action.getEvent();

	NM_LISTVIEW* nmlistv = (NM_LISTVIEW*)event.getLParam();

	putting = TRUE;
	getting = FALSE;

	dragging = TRUE;
	localLastChangedItem = nmlistv->iItem;		
	POINT current;

	HIMAGELIST img = localFile -> createDragImage( 
								localLastChangedItem, &current);
	if(imgList) {
		delete imgList;
	}	
	imgList = new ImageList(img);
	
	POINT  client = nmlistv -> ptAction;
	POINT  hotSpot;
	hotSpot.x = client.x - current.x;
	hotSpot.y = client.y - current.y;
	imgList -> beginDrag(0, hotSpot.x, hotSpot.y);
	
	POINT  screen;
	getCursorPos(&screen);

	imgList -> dragEnter(screen.x, screen.y);

	capture();
}


void SolFtp::remoteFileDrag(Action& action)
{
	Event& event = action.getEvent();
	NM_LISTVIEW* nmlistv = (NM_LISTVIEW*)event.getLParam();

	getting = TRUE;
	putting  = FALSE;

	dragging = TRUE;
	remoteLastChangedItem = nmlistv->iItem;		
	POINT current;

	HIMAGELIST img = remoteFile -> createDragImage( 
								remoteLastChangedItem, &current);
	if(imgList) {
		delete imgList;
	}
	imgList = new ImageList(img);
	
	POINT  client = nmlistv -> ptAction;

	POINT  hotSpot;

	hotSpot.x = client.x - current.x;
	hotSpot.y = client.y - current.y;
	imgList -> beginDrag(0, hotSpot.x, hotSpot.y);
	
	POINT screen;
	getCursorPos(&screen);

	imgList -> dragEnter(screen.x, screen.y);

	capture();
}

void SolFtp::putLocalFiles(char* filename)
{
	XPrintf("PutLocalFiles %s\r\n", filename);

	char mode[10];
	mode[0] = Zero;
	if(binary -> getCheck()) {
		sprintf(mode, "TYPE I\r\n");
		sendCommand(mode);
	}
	if(ascii -> getCheck()) {
		sprintf(mode, "TYPE A\r\n");
		sendCommand(mode);
	}

	if(listenSocket) {
		delete listenSocket;
		listenSocket = NULL;
	}

	if ((listenSocket = createListenSocket()) == NULL) {
		XPrintf("CreateListenSocket failed \r\n");
		return;
	}

	char command[_MAX_PATH+10];
	sprintf(command, "STOR %s\r\n", filename);
	sendCommand(command);
			
	if (acceptDataConnection() == FALSE) {
		XPrintf("AcceptDataConnection failed \r\n");
		return;
	}
	char dir[_MAX_PATH];
	localLabel -> getText(dir, sizeof(dir));
	strcat(dir, "\\");
	strcat(dir, filename);
	writeData(dir);

	delete dataChannel;
	dataChannel = NULL;
} 


void SolFtp::getRemoteFiles(char* filename)
{
	char mode[10];
	mode[0] = Zero;
	if(binary -> getCheck()) {
		sprintf(mode, "TYPE I\r\n");
		sendCommand(mode);
	}
	if(ascii -> getCheck()) {
		sprintf(mode, "TYPE A\r\n");
		sendCommand(mode);
	}

	XPrintf("GetRemoteFiles %s \r\n", filename);
	if(listenSocket){
		delete listenSocket;
	}

	if ((listenSocket = createListenSocket()) == NULL) {
		return;
	}

	char command[_MAX_PATH+10];
	sprintf(command, "RETR %s\r\n", filename);
	sendCommand(command);
			
	if(acceptDataConnection() == FALSE) {
		XPrintf("AcceptDataConnection failed\n");
		return;
	}
	char fullpath[_MAX_PATH];
	localLabel -> getText(fullpath, sizeof(fullpath));

	if(strlen(fullpath)) {
		strcat(fullpath, "\\");
		strcat(fullpath, filename);
		XPrintf("fullpathname %s\r\n", fullpath);
		readData(fullpath);
	}

	delete dataChannel;
	dataChannel = NULL;
} 


long SolFtp::lButtonUp(Event& event)
{
	LPARAM lParam = event.getLParam();
	if(dragging) {
		dragging = FALSE;
		imgList -> dragLeave(this);
		imgList -> endDrag();
		delete imgList;
		imgList = NULL;
		releaseCapture();
		showCursor(TRUE);

		POINT pt;
		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);
		toScreen(&pt);
		if(::WindowFromPoint(pt) == localFile->getWindow() && getting) {
			LV_ITEM hitem;
			memset(&hitem, 0, sizeof(hitem));

			char* text       = new char[_MAX_PATH];
			hitem.mask       = LVIF_TEXT;
			hitem.iItem      = remoteLastChangedItem;
			hitem.pszText    = text;
			hitem.cchTextMax = _MAX_PATH;
			remoteFile -> getItem(&hitem);
				
			if(MessageBox(NULL, "Getting a remoteFile", text, MB_OKCANCEL) ==
				IDOK) {
				getRemoteFiles(text);

				char dir[_MAX_PATH];
				dir[0] = Zero;
				//getcwd(dir, sizeof(dir));
				localLabel -> getText(dir, sizeof(dir));
				localFile  -> deleteAllItems();
				localFile  -> findFiles(dir);

			}
					
			delete [] text;
		}
		if(::WindowFromPoint(pt) == remoteFile->getWindow() && putting) {
			LV_ITEM hitem;
			memset(&hitem, 0, sizeof(hitem));

			char* text       = new char[_MAX_PATH];
			hitem.mask       = LVIF_TEXT;
			hitem.iItem      = localLastChangedItem;
			hitem.pszText    = text;
			hitem.cchTextMax = _MAX_PATH;
			localFile -> getItem(&hitem);

			if(MessageBox(NULL, "Putting a localFile", text, MB_OKCANCEL)
					== IDOK) {
				putLocalFiles(text);
				remoteFile -> deleteAllItems();
				listupRemoteFiles();
			}
			delete [] text;
		}
	  }

    return 0L;
}


long SolFtp::mouseMove(Event& event)
{
	if(dragging) {

		LPARAM lParam = event.getLParam();

		POINT pt;
		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);
		toScreen(&pt);
						
		if(::WindowFromPoint(pt) != localFile->getWindow() && getting) {
			if(GetCursor() != LoadCursor(NULL, IDC_NO)) { 
				SetCursor(LoadCursor(NULL, IDC_NO));
			}
		}
		else		
			if(::WindowFromPoint(pt) != remoteFile->getWindow() && putting) {
			if(GetCursor() != LoadCursor(NULL, IDC_NO)) { 
				SetCursor(LoadCursor(NULL, IDC_NO));
			}
		}
		else {
			if(GetCursor() != LoadCursor(NULL, IDC_ARROW)) { 
				SetCursor(LoadCursor(NULL, IDC_ARROW));
			}
		}
		imgList -> dragMove(pt.x, pt.y);
	}
	return 0L;
}



long SolFtp::size(Event& event)
{
	ApplicationView::size(event);

	ClientDC dc(this);
	int th = dc.getTextHeight();

	LPARAM lParam = event.getLParam();
	StatusBar* statusBar = getStatusBar();
	int sh = statusBar -> getHeight();

	int top = 32+th;
	int w = LOWORD(lParam);
	int h = (HIWORD(lParam)-top-sh)*1/2;
	binary -> reshape(w-200, 2, 80, th);
	ascii  -> reshape(w-200+100, 2, 80, th);

	//localTree
	localLabel -> reshape(2, 32, w-4, th);
	if(localTree) {
		localTree -> reshape(0, top, w/2-2, h-th/2-4);
	}

	//localFiles
	if(localFile) {
		localFile->	reshape(w/2+2, top, w/2-2, h-th/2-4);

	}

	remoteLabel->reshape(2, top+h-th/2, w-4, th);

	//remoteTree
	if(remoteTree) {
		remoteTree->reshape(0, top+h+th/2, w/2-2, h-th/2); 

	}

	// remoteFiles
	if(remoteFile) {
		remoteFile -> reshape(w/2+2, top+h+th/2, w/2-2,h-th/2); 
	}
	return 0L;
}
     


void	Main(int argc, char** argv)
{
	const char* name = "SOLFTP";
	Application applet(name, argc, argv);

	Args args;
	args.set(XmNbackground, (ulong)(COLOR_BTNFACE+1));
	args.set(XmNdefaultStatusBar, TRUE);
	args.set(XmNmenuName, name);
	SolFtp solFtp(applet, name, args);
	solFtp.realize();
	applet.run();
}

