#include"main.h"

char *szFEMode[] =
{
	"Recv file",
	"Send file"
};

#define USE_BUILDIN_BASE64
//
//  BASE64 encoding/decoding
//
#define Base64_GetDecodedBufferSize(cchEncoded)  (((cchEncoded)>>2)*3)
#define Base64_GetEncodedBufferSize(cbDecoded)  (((cbDecoded)*4+11)/12*4+1)
#ifdef USE_BUILDIN_BASE64
#define Base64_Encode(nlb64) CallService(MS_NETLIB_BASE64ENCODE, 0, (LPARAM)nlb64)
#define Base64_Decode(nlb64) CallService(MS_NETLIB_BASE64DECODE, 0, (LPARAM)nlb64)
#else

static char base64chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define Base64_Encode(nlb64) NetlibBase64Encode(0, (LPARAM)nlb64)
#define Base64_Decode(nlb64) NetlibBase64Decode(0, (LPARAM)nlb64)

int NetlibBase64Encode(WPARAM wParam,LPARAM lParam)
{
	NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
	int iIn;
	char *pszOut;
	PBYTE pbIn;

	if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return 0;
	}
	if(nlb64->cchEncoded<Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded)) {
		SetLastError(ERROR_BUFFER_OVERFLOW);
		return 0;
	}
	nlb64->cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded);
	for(iIn=0,pbIn=nlb64->pbDecoded,pszOut=nlb64->pszEncoded;iIn<nlb64->cbDecoded;iIn+=3,pbIn+=3,pszOut+=4) {
		pszOut[0]=base64chars[pbIn[0]>>2];
		if(nlb64->cbDecoded-iIn==1) {
			pszOut[1]=base64chars[(pbIn[0]&3)<<4];
			pszOut[2]='=';
			pszOut[3]='=';
			pszOut+=4;
			break;
		}
		pszOut[1]=base64chars[((pbIn[0]&3)<<4)|(pbIn[1]>>4)];
		if(nlb64->cbDecoded-iIn==2) {
			pszOut[2]=base64chars[(pbIn[1]&0xF)<<2];
			pszOut[3]='=';
			pszOut+=4;
			break;
		}
		pszOut[2]=base64chars[((pbIn[1]&0xF)<<2)|(pbIn[2]>>6)];
		pszOut[3]=base64chars[pbIn[2]&0x3F];
	}
	pszOut[0]='\0';
	return 1;
}

static BYTE Base64CharToInt(char c)
{
	if(c>='A' && c<='Z') return c-'A';
	if(c>='a' && c<='z') return c-'a'+26;
	if(c>='0' && c<='9') return c-'0'+52;
	if(c=='+') return 62;
	if(c=='/') return 63;
	if(c=='=') return 64;
	return 255;
}

int NetlibBase64Decode(WPARAM wParam,LPARAM lParam)
{
	NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
	char *pszIn;
	PBYTE pbOut;
	BYTE b1,b2,b3,b4;
	int iIn;

	if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return 0;
	}
	if(nlb64->cchEncoded&3) {
		SetLastError(ERROR_INVALID_DATA);
		return 0;
	}
	if(nlb64->cbDecoded<Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded)) {
		SetLastError(ERROR_BUFFER_OVERFLOW);
		return 0;
	}
	nlb64->cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded);
	for(iIn=0,pszIn=nlb64->pszEncoded,pbOut=nlb64->pbDecoded;iIn<nlb64->cchEncoded;iIn+=4,pszIn+=4,pbOut+=3) {
		b1=Base64CharToInt(pszIn[0]);
		b2=Base64CharToInt(pszIn[1]);
		b3=Base64CharToInt(pszIn[2]);
		b4=Base64CharToInt(pszIn[3]);
		if(b1==255 || b1==64 || b2==255 || b2==64 || b3==255 || b4==255) {
			SetLastError(ERROR_INVALID_DATA);
			return 0;
		}
		pbOut[0]=(b1<<2)|(b2>>4);
		if(b3==64) {nlb64->cbDecoded-=2; break;}
		pbOut[1]=(b2<<4)|(b3>>2);
		if(b4==64) {nlb64->cbDecoded--; break;}
		pbOut[2]=b4|(b3<<6);
	}
	return 1;
}
#endif

char* ltoax(char* s, DWORD value)
{
	if(value == 0)
	{
		*s++ = '0';
	}
	uchar data;
	int indx = 8;
	while(indx && !(data = (uchar)(value >> 28) & 0x0F))
	{
		value <<= 4;
		indx--;
	}
	while(indx)
	{
		data = (uchar)(value >> 28) & 0x0F;
		if(data > 9) data += 'A' - 10;
		else data += '0';
		*s++ = data;
		value <<= 4;
		indx--;
	}
	return s;
}
uint atolx(char* &value)
{
	uint result = 0;
	uchar ch;

	while( *value && (ch = *value - '0') >= 0 )
	{
		if(ch > 9)
		{
			ch -= 'A' - '0';
			if(ch > 5) break;
			ch += 10;
		}
		result = result * 16 + ch;
		value++;
	}	
	return result;
}

char cCmdList[CMD_COUNT] =
{
	'?',
	'+',
	'-',
	
	'*',

	'>',
	'!',
	'.'
};

static int CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = PUGetContact(hWnd);
	HWND hDlg = (HWND)PUGetPluginData(hWnd);
/*
	if(hContact)
	{
		CLISTEVENT *lpcle;
		int indx = 0;
		for(;;)
		{
			if((lpcle = (CLISTEVENT*)CallService(MS_CLIST_GETEVENT, (WPARAM)hContact, indx)) == NULL)
				break;
			if(lstrcmp(lpcle->pszService, SERVICE_NAME "/FERecvFile") == 0)
			{
				lpcle->lParam = (LPARAM)hWnd;
				break;
			}
			indx++;
		}
	}
*/
	switch(message) {
		case WM_COMMAND:
		{
			PUDeletePopUp(hWnd);
			CallService(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)0);

			if(IsWindow(hDlg))
			{
				ShowWindow(hDlg, SW_SHOWNORMAL);
				SetForegroundWindow(hDlg);
				SetFocus(hDlg);
			}

			break;
		}
		case WM_CONTEXTMENU:
			PUDeletePopUp(hWnd);
			break;
		case UM_FREEPLUGINDATA:
			return TRUE; //TRUE or FALSE is the same, it gets ignored.
		default:
			break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}
//
// Just create simple Popup for specified contact
//
void MakePopupMsg(HWND hDlg, HANDLE hContact, char *msg)
{
	HWND hFocused = GetForegroundWindow();
	if(hDlg == hFocused || hDlg == GetParent(hFocused)) return;

	POPUPDATAEX ppd;
	//
	//The text for the second line. You could even make something like: char lpzText[128]; lstrcpy(lpzText, "Hello world!"); It's your choice.
	//
	ZeroMemory(&ppd, sizeof(ppd)); //This is always a good thing to do.
	ppd.lchContact = (HANDLE)hContact; //Be sure to use a GOOD handle, since this will not be checked.
	ppd.lchIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SMALLICON));
	lstrcpy(ppd.lpzContactName, (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0));
	lstrcpy(ppd.lpzText, msg);
	ppd.colorBack = GetSysColor(COLOR_INFOBK);
	ppd.colorText = GetSysColor(COLOR_INFOTEXT);
	ppd.PluginWindowProc = (WNDPROC)PopupDlgProc;
	ppd.PluginData = (void*)hDlg;
	ppd.iSeconds = -1;

	CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd, 0);
}
//
// Get ID of string message
//
int getMsgId(char *msg)
{
	for(int indx = 0; indx < CMD_COUNT; indx++)
	{
		if(*msg == cCmdList[indx]) return indx;
	}
	return -1;
};

int RetrieveFileSize(char *filename)
{
	int handle = open(filename, O_RDONLY|O_BINARY,0);
	if(handle != -1)
	{
		int size = filelength(handle);
		close(handle);
		return size;
	}
	return handle;
}

FILEECHO::FILEECHO(HANDLE Contact)
{
	hContact = Contact;
	dwSendInterval = DBGetContactSettingDword(NULL, SERVICE_NAME, "SendDelay", 6000);
	//dwChunkSize = DBGetContactSettingDword(NULL, SERVICE_NAME, "ChunkSize", 5000);

	chunkMaxLen = DBGetContactSettingDword(NULL, SERVICE_NAME, "ChunkSize", 5000);
	chunkCount = 0;
	filename = NULL;

	rgbRecv = DBGetContactSettingDword(NULL, SERVICE_NAME, "colorRecv", RGB(64,255,64));
	rgbSent = DBGetContactSettingDword(NULL, SERVICE_NAME, "colorSent", RGB(255,255,64));
	rgbUnSent = DBGetContactSettingDword(NULL, SERVICE_NAME, "colorUnsent", RGB(128,128,128));
	rgbToSend = DBGetContactSettingDword(NULL, SERVICE_NAME, "colorTosend", RGB(192,192,192));
	asBinary = DBGetContactSettingDword(NULL, SERVICE_NAME, "base64", 1) == 0;
}

uint controlEnabled[][2] =
{
 	IDC_PLAY,
		STATE_OPERATE|STATE_PAUSED|STATE_PRERECV|STATE_ACKREQ|STATE_IDLE,
	IDC_STOP,
		STATE_OPERATE|STATE_PAUSED|STATE_PRERECV|STATE_REQSENT|STATE_ACKREQ,
//	IDC_FILENAME,
//		STATE_IDLE|STATE_PRERECV|STATE_FINISHED|STATE_CANCELLED,
//	IDC_BROWSE,
//		STATE_IDLE|STATE_PRERECV|STATE_FINISHED|STATE_CANCELLED,
};
/*
char *stateMsg[][2] =
{
	(char*)STATE_IDLE,"Idle",
	(char*)STATE_REQSENT,"ReqSent",
	(char*)STATE_PRERECV,"PreRecv",
	(char*)STATE_OPERATE,"Operate",
	(char*)STATE_ACKREQ,"AckReq",
	(char*)STATE_CANCELLED,"Cancelled",
	(char*)STATE_FINISHED,"Finished",
	(char*)STATE_PAUSED,"Paused"
};
*/

char *hint_controls[4] = {
	"Perform",
	"Pause",
	"Revive a transfer",
	"Stop"
};

void FILEECHO::setState(DWORD state)
{
	iState = state;
	int indx;

	for(indx = 0; indx < SIZEOF(controlEnabled); indx++)
	{
		EnableWindow(GetDlgItem(hDlg, controlEnabled[indx][0]), (iState & controlEnabled[indx][1]) != 0);
	}

	if(!inSend) // recv
	{
		int kind;
		SendDlgItemMessage(hDlg, IDC_FILENAME, EM_SETREADONLY, (state != STATE_PRERECV), 0);
		EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), (iState & (STATE_PRERECV|STATE_FINISHED)));
		//SendDlgItemMessage(hDlg, IDC_FILENAME, EM_SETREADONLY, (iState & STATE_PRERECV) == 0, 0);
		//EnableWindow(GetDlgItem(hDlg, IDC_FILENAME), (iState == STATE_PRERECV));
		//EnableWindow(GetDlgItem(hDlg, IDC_FILENAME), (iState & STATE_IDLE|STATE_PRERECV|STATE_FINISHED|STATE_CANCELLED) != 0);
		if(state & (STATE_IDLE|STATE_FINISHED|STATE_CANCELLED|STATE_PRERECV))
			kind = ICON_PLAY;
		else
			kind = ICON_REFRESH;
		SendDlgItemMessage(hDlg, IDC_PLAY,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[kind]);
		SendDlgItemMessage(hDlg, IDC_PLAY, BUTTONADDTOOLTIP,(WPARAM)Translate(hint_controls[kind]),0);
	}
	else
	{
		SendDlgItemMessage(hDlg, IDC_FILENAME, EM_SETREADONLY, (iState & (STATE_IDLE|STATE_FINISHED|STATE_CANCELLED)) == 0, 0);
		EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), (iState & (STATE_IDLE|STATE_FINISHED|STATE_CANCELLED)) != 0);
		//EnableWindow(GetDlgItem(hDlg, IDC_FILENAME), (iState & STATE_IDLE|STATE_PRERECV|STATE_FINISHED|STATE_CANCELLED) != 0);
		switch(state)
		{
			case STATE_FINISHED:
			case STATE_CANCELLED:
			case STATE_IDLE:
			case STATE_PAUSED:
				EnableWindow(GetDlgItem(hDlg, IDC_PLAY), TRUE);
				SendDlgItemMessage(hDlg, IDC_PLAY, BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[ICON_PLAY]);
				SendDlgItemMessage(hDlg, IDC_PLAY, BUTTONADDTOOLTIP,(WPARAM)Translate(hint_controls[ICON_PLAY]),0);
				break;
			case STATE_OPERATE:
				SendDlgItemMessage(hDlg, IDC_PLAY, BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[ICON_PAUSE]);
				SendDlgItemMessage(hDlg, IDC_PLAY, BUTTONADDTOOLTIP,(WPARAM)Translate(hint_controls[ICON_PAUSE]),0);
				break;
		}
	}
	updateProgress();
}

void FILEECHO::updateTitle()
{
	char newtitle[256], *contactName;

	contactName=(char*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hContact,0);
	if(iState == STATE_OPERATE && chunkCount != 0)
		_snprintf(newtitle,sizeof(newtitle),"%d%% - %s: %s",chunkSent * 100 / chunkCount, Translate(szFEMode[inSend]), contactName);
	else
		_snprintf(newtitle,sizeof(newtitle),"%s: %s",Translate(szFEMode[inSend]), contactName);
	SetWindowText(hDlg, newtitle);
}

void BuildFreqTable(uchar *data, uint len, uint *freqTable)
{
	ZeroMemory(freqTable, 256*sizeof(uint));
	for(uint indx = 0; indx < len; indx++)
		freqTable[data[indx]]++;
}

int FILEECHO::createTransfer()
{
	uint LastError;
	hFile = INVALID_HANDLE_VALUE;
	hMapping = NULL;
	lpData = NULL;
#ifdef DEBUG
	overhead = 0;
#endif
	hFile = CreateFile(filename, inSend?GENERIC_READ:(GENERIC_READ|GENERIC_WRITE), inSend?FILE_SHARE_READ:0,
		NULL, inSend?OPEN_EXISTING:(DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0)?CREATE_ALWAYS:CREATE_NEW), FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile == INVALID_HANDLE_VALUE && !inSend && GetLastError() == ERROR_FILE_EXISTS)
	{
		if(MessageBox(hDlg, Translate("File already exists. Overwrite?"),
			Translate(SERVICE_TITLE),
			MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) != IDYES) return 0;
		hFile = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0,
			NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	}
	if(hFile == INVALID_HANDLE_VALUE) goto createTransfer_FAILED;
	if(!inSend)
	{
		SetFilePointer(hFile, fileSize, NULL, FILE_CURRENT);
		SetEndOfFile(hFile);
	}
	else
		fileSize = GetFileSize(hFile, NULL);
	hMapping = CreateFileMapping(hFile, NULL, inSend?PAGE_READONLY:PAGE_READWRITE,
		0, fileSize, NULL);
	LastError = GetLastError();
	if(hMapping == NULL) goto createTransfer_FAILED;
	lpData = (uchar*)MapViewOfFile(hMapping, inSend?FILE_MAP_READ:FILE_MAP_WRITE, 0,0,0);
	LastError = GetLastError();
	if(lpData == NULL) goto createTransfer_FAILED;

	if(inSend)
	//
	// frequency analysis of source file
	// and building the table of offsets
	//
	{
		if(asBinary)
		{
			uint freq_table[256];
			uchar *data;
			uint len, chunk_offset, chunk_size, out_size, indx;
			int chunk_count_limit;

			codeSymb = 1;
			//
			// searching for symbol with lowest frequency: "codeSymb"
			//
			BuildFreqTable(lpData, fileSize, freq_table);
			for(int indx = codeSymb+1; indx < 256; indx++)
			{
				if(freq_table[codeSymb] > freq_table[indx]) codeSymb = indx;
			}
			//DEBUG
			//codeSymb = ':';
			
			//
			// calculating chunks sizes
			// build table of chunks offsets: chunkPos
			//
			chunk_count_limit = 2*fileSize/chunkMaxLen+2;
			chunkPos = (uint*)malloc(sizeof(uint)*chunk_count_limit);
			data = lpData;
			chunk_size = 0; out_size = 0; indx = 0; chunk_offset = 0;
			for(len = fileSize; len; len--)
			{
				if(*data == 0 || *data == codeSymb)
					out_size += 2;
				else
					out_size++;

				data++; chunk_size++;
				if(out_size >= chunkMaxLen-1)
				{
					chunkPos[indx] = chunk_offset; chunk_offset += chunk_size;
					chunk_size = 0; out_size = 0;
					indx++;
				}
			}
			chunkPos[indx++] = chunk_offset; chunkCount = indx;
			chunkPos = (uint*)realloc(chunkPos, sizeof(uint)*(chunkCount+1));
			chunkPos[indx] = chunk_offset + chunk_size;
		}
		else
		{
			int EncodedMaxLen = Base64_GetEncodedBufferSize(Base64_GetDecodedBufferSize(chunkMaxLen));
			int DecodedMaxLen = Base64_GetDecodedBufferSize(EncodedMaxLen);
			int indx = 0;

			codeSymb = '-';
			chunkCount = (fileSize + DecodedMaxLen - 1) / DecodedMaxLen;
			chunkPos = (uint*)malloc(sizeof(uint)*(chunkCount+1));
			for(uint chunk_offset = 0, indx = 0; indx < chunkCount; indx++, chunk_offset += DecodedMaxLen)
				chunkPos[indx] = chunk_offset;
			chunkPos[indx] = chunkPos[indx-1] + fileSize%DecodedMaxLen;
		}
	}
	else
		chunkCount = chunkCountx;
	chunkAck = (uchar*)malloc(sizeof(uchar)*chunkCount);
	memset(chunkAck, 0, sizeof(uchar)*chunkCount);

	chunkIndx = 0; chunkSent = 0;
	
	return 1;
createTransfer_FAILED:
	if(lpData != NULL) UnmapViewOfFile(lpData);
	if(hMapping != NULL) CloseHandle(hMapping);
	if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
	return 0;
}

void FILEECHO::destroyTransfer()
{
	if(chunkCount)
	{
		chunkCount = 0;
		if(inSend)
			free(chunkPos);
		free(chunkAck);
		if(lpData != NULL) UnmapViewOfFile(lpData);
		if(hMapping != NULL) CloseHandle(hMapping);
		if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
	}
	//setState(STATE_IDLE);
	return;
}

void FILEECHO::sendReq()
{

	char sendbuf[MAX_PATH];
	
	if(!createTransfer())
	{
		SetDlgItemText(hDlg, IDC_FILESIZE, Translate("Couldn't open a file"));
		return;
	}
	///!!!!!!!
	char *p = filename + strlen(filename);
	while(p != filename && *p != '\\')
		p--;
	if(*p == '\\')
		strcpy(filename,p+1);

	_snprintf(sendbuf, sizeof(sendbuf), Translate("Size: %d bytes"), fileSize);
	SetDlgItemText(hDlg, IDC_FILESIZE, sendbuf);
	_snprintf(sendbuf, sizeof(sendbuf), "?%c%c%d:%d " NOPLUGIN_MESSAGE, asBinary+'0', codeSymb, chunkCount, fileSize);
	sendCmd(0, CMD_REQ, sendbuf, filename);

	SetDlgItemText(hDlg, IDC_STATUS, Translate("Request sent. Awaiting of acceptance.."));
	setState(STATE_REQSENT);
}
void FILEECHO::incomeRequest(char *param)
{
	// param: filename?cCOUNT:SIZE
	char buf[MAX_PATH];
	// param == &filename
	char *p = strchr(param, '?');
	if(p == NULL) return; *p++ = 0;
	CallService(MS_FILE_GETRECEIVEDFILESFOLDER, (WPARAM)hContact, (LPARAM)buf);
	strncat(buf, param, sizeof(buf));
	if(filename) free(filename);
	filename = strdup(buf);
	// p == &c
	if(*p == 0) return; asBinary = (*p++) != '0';
	if(*p == 0) return; codeSymb = *p++;
	// p == &COUNT
	if(*p == 0) return; param = strchr(p, ':');
	// param == &SIZE
	if(param == NULL) return; *param++ = 0;
	if(*param == 0) return;
	chunkCountx = atoi(p);
	fileSize = atoi(param);

	_snprintf(buf, sizeof(buf), Translate("Size: %d bytes"), fileSize);
	SetDlgItemText(hDlg, IDC_FILENAME, filename);
	SetDlgItemText(hDlg, IDC_FILESIZE, buf);

	setState(STATE_PRERECV);
	inSend = FALSE;

	SkinPlaySound("RecvFile");
	int AutoMin = DBGetContactSettingByte(NULL,"SRFile","AutoMin",0);
	if(DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0) && !DBGetContactSettingByte((HANDLE)hContact,"CList","NotOnList",0))
	{
		PostMessage(hDlg, WM_COMMAND, IDC_PLAY, 0);
		if(AutoMin)
			ShowWindow(hDlg, SW_SHOWMINIMIZED);
//		ShowWindow(hDlg, SW_MINIMIZE);
//		UpdateWindow(hDlg);
	}
//	else
	if(!IsWindowVisible(hDlg) && !AutoMin)
	{
		CLISTEVENT cle;
		ZeroMemory(&cle, sizeof(cle));
		cle.cbSize = sizeof(cle);
		cle.hContact = hContact;
		cle.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SMALLICON));
		cle.flags = CLEF_URGENT;
		cle.hDbEvent = 0;
		cle.pszService = SERVICE_NAME "/FERecvFile";
		CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);

		MakePopupMsg(hDlg, hContact, "Incoming file...");
	}
}

void FILEECHO::cmdACCEPT()
{
	if(chunkCount == 0) return;
	setState(STATE_OPERATE);
	SetDlgItemText(hDlg, IDC_STATUS, Translate("Sending..."));
	lastTimestamp = GetTickCount();
	//PostMessage(hDlg, WM_TIMER, 0,0);
	//onSendTimer();
	SetTimer(hDlg, TIMER_SEND, dwSendInterval, 0);
}

void FILEECHO::updateProgress()
{
	InvalidateRect(GetDlgItem(hDlg, IDC_PROGRESS), NULL, TRUE);
	updateTitle();
}
//
// called in receive mode
// used to transfer acknowledge
//
void FILEECHO::onRecvTimer()
{
	if(chunkCount == 0) return;
	char *buffer = (char*)malloc(1024);
	char *p = buffer;
	uchar prev_value;
	uint indx, jndx;
	
	KillTimer(hDlg, TIMER_SEND);
	//
	// Build response about successfully received chunks
	//
	indx = jndx = 0; prev_value = chunkAck[jndx];
	while(jndx < chunkCount)
	{
		if(chunkAck[jndx] != prev_value)
		{
			if(prev_value != CHUNK_ACK)
			{
				p = ltoax(p, indx);
				if(indx != jndx-1)
				{
					*p++ = '-';
					p = ltoax(p, jndx-1);
				}
				*p++ = ',';
			}
			indx = jndx;
			prev_value = chunkAck[jndx];
		}
		jndx++;
	}
	if(prev_value != CHUNK_ACK)
	{
		p = ltoax(p, indx);
		if(indx != jndx-1)
		{
			*p++ = '-';
			p = ltoax(p, jndx-1);
		}
	}
	*p = 0;
	if(*buffer == 0)
	{
		char *msg = Translate("Received successfully");
		SetDlgItemText(hDlg, IDC_STATUS, msg);
		MakePopupMsg(hDlg, hContact, msg);
		setState(STATE_FINISHED);
		if(DBGetContactSettingByte(NULL,"SRFile","AutoClose",0))
		{
			PostMessage(hDlg, WM_CLOSE, 0,0);
			CallService(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)0);
		}
		SkinPlaySound("FileDone");
		destroyTransfer();
		buffer[0] = 'x'; buffer[1] = 0;
	}
	sendCmd(0, CMD_DACK, buffer);
	free(buffer);
	//if(iState != STATE_FINISHED) SetTimer(hDlg, TIMER_SEND, lastDelay*2, 0);
}
//
// called in sending mode
// used to data transfer and
// sending of scheduled commands
//
void FILEECHO::onSendTimer()
{
	if(chunkCount == 0) return;
	//
	// perform request of acknowledge, if scheduled
	//
	KillTimer(hDlg, TIMER_SEND);
	//
	// Search for next unsent chunk
	//
	while(chunkIndx < chunkCount && chunkAck[chunkIndx] != CHUNK_UNSENT) chunkIndx++;
	if(iState == STATE_ACKREQ || chunkIndx == chunkCount)
	{
		SetDlgItemText(hDlg, IDC_STATUS, Translate("Requesting of missing chunks"));
		setState(STATE_OPERATE);
		sendCmd(0, CMD_END, "", NULL);
		chunkIndx = chunkCount+1;
		return;
	}
	if(chunkIndx > chunkCount) return;

	uchar *buffer = (uchar*)malloc(chunkMaxLen*2);
	uchar *p = buffer;
	uchar *data = lpData + chunkPos[chunkIndx];
	uchar *data_end = lpData + chunkPos[chunkIndx+1];
	ulong chksum = memcrc32(data, data_end - data, INITCRC);

	if(asBinary)
	{
		//
		// Encoding data to transfer with symb. filtering
		//
		while(data < data_end)
		{
			uchar ch = *data++;
			if(ch == 0)
			{
				*p++ = codeSymb; *p++ = '0';
			}
			else if (ch == codeSymb)
			{
				*p++ = codeSymb; *p++ = '+';
			}
			else
				*p++ = ch;
		}
		*p = 0;
	}
	else
	{
		NETLIBBASE64 nlb;

		nlb.pbDecoded = data;
		nlb.cbDecoded = data_end - data;
		nlb.pszEncoded = (char*)buffer;
		nlb.cchEncoded = chunkMaxLen*2;
		
		Base64_Encode(&nlb);
	}

	char prefix[128];
	_snprintf(prefix, sizeof(prefix), "%X,%X,%X>", chunkIndx+1, chunkPos[chunkIndx], chksum);
#ifdef DEBUG
	overhead += lstrlen((char*)buffer);
#endif
	sendCmd(0, CMD_DATA, (char*)buffer, (char*)prefix);
	chunkAck[chunkIndx] = CHUNK_SENT;

	free(buffer);

	chunkIndx++; chunkSent++;

	if(chunkIndx == chunkCount)
		setState(STATE_ACKREQ);
	else
	{
		SetDlgItemText(hDlg, IDC_STATUS, Translate("Sending..."));
		updateProgress();
	}
	SetTimer(hDlg, TIMER_SEND, dwSendInterval, 0);
}
void FILEECHO::cmdDATA(char *param)
{
	if(chunkCount == 0) return;
	chunkIndx = atolx(param); param++;
	if(chunkIndx-- == 0) return;
	uint filepos = atolx(param); param++;
	if(filepos >= fileSize) return;

	ulong chksum_local;
	ulong chksum_remote = atolx(param); param++;

	KillTimer(hDlg, TIMER_SEND);

	//
	// Decoding of incoming data
	//
	uchar *data = lpData + filepos;
	uchar *data_end = lpData + fileSize;
	if(asBinary)
	{
		uchar ch;
		while(ch = *param++)
		{
			if(ch == codeSymb)
			{
				if((ch = *param++) == 0) goto cmdDATA_corrupted;
				switch(ch)
				{
					case '+':
						ch = codeSymb;
						break;
					case '0':
						ch = 0;
						break;
					default:
						goto cmdDATA_corrupted;
				}
			}
			if(data > data_end) goto cmdDATA_corrupted;
			*data++ = ch;
		}
	}
	else
	{
		NETLIBBASE64 nlb;
		uchar *temp_buffer;

		nlb.pszEncoded = param;
		nlb.cchEncoded = (int)_tcslen(param);
		temp_buffer = (uchar*)malloc(nlb.cchEncoded);
		nlb.pbDecoded = temp_buffer;
		nlb.cbDecoded = nlb.cchEncoded;
		
		Base64_Decode(&nlb);
		memcpy(data, temp_buffer, min(nlb.cbDecoded, data_end - data));
		data += nlb.cbDecoded;		
	}
	//
	// let's check it up
	//
	chksum_local = memcrc32(lpData + filepos, data - (lpData + filepos), INITCRC);
	if(chksum_local == chksum_remote)
	{
		if(chunkAck[chunkIndx] != CHUNK_ACK) chunkSent++;
		chunkAck[chunkIndx] = CHUNK_ACK;
		//chunkPos[chunkIndx++] = filepos;
	}
	SetDlgItemText(hDlg, IDC_STATUS, Translate("Receiving..."));
	updateProgress();
cmdDATA_corrupted:
	//SetTimer(hDlg, TIMER_SEND, lastDelay*2, 0);
	;
}
void FILEECHO::cmdEND()
{
	SetTimer(hDlg, TIMER_SEND, dwSendInterval, 0);
}
void FILEECHO::cmdDACK(char *param)
{
	uint indx, jndx;

	if(chunkCount == 0) return;
	memset(chunkAck, CHUNK_ACK, sizeof(uchar)*chunkCount);
	if(*param == 'x')
	//
	// All chunks has been received successfully
	//
	{
#ifdef DEBUG
		char msg[100];

		_snprintf(msg, sizeof(msg), "overhead: %d", overhead);
		SetDlgItemText(hDlg, IDC_STATUS, msg);
#else
		char *msg = Translate("Sent successfully");
		SetDlgItemText(hDlg, IDC_STATUS, msg);
#endif
		SkinPlaySound("FileDone");
		destroyTransfer();
		MakePopupMsg(hDlg, hContact, msg);
		setState(STATE_FINISHED);
		return;
	}
	chunkSent = chunkCount;
	//
	// Mark chunks to re-transfer,
	// according received info
	// 
	// format: chunk1, chunk3-chunk10, etc..
	//
	while(*param)
	{
		indx = atolx(param);
		if(*param == '-')
		{
			param++; jndx = atolx(param);
		}
		else
			jndx = indx;
		if(*param == 0 || *param == ',')
		{
			for(uint p = indx; p <= jndx; p++)
			{
				if(p < chunkCount)
				{
					chunkAck[p] = CHUNK_UNSENT;
					chunkSent--;
				}
			}
			if(*param == ',')
				param++;
		}
	}
	updateProgress();

	//
	// retransfer some parts
	//
	chunkIndx = 0;
	SetTimer(hDlg, TIMER_SEND, dwSendInterval, 0);
}

void FILEECHO::perform(char *str)
{
	int msgId = getMsgId(str);
	if(msgId == -1)
	{
		MakePopupMsg(hDlg, hContact, Translate("Unknown command for \"" SERVICE_TITLE "\" was received"));
		return;
	}
	if(inSend)
		switch(msgId)
		{
			case CMD_REQ:
				if(MessageBox(hDlg, Translate("Incoming file request. Do you want proceed?"),
					Translate(SERVICE_TITLE), MB_YESNO | MB_ICONWARNING) == IDYES)
				{
					SetDlgItemText(hDlg, IDC_STATUS, "");
					SendMessage(hDlg, WM_COMMAND, IDC_STOP, 0);

					incomeRequest(str+1);
					updateTitle();
					break;
				}
				break;
			case CMD_ACCEPT:
				cmdACCEPT();
				break;
			case CMD_CANCEL:
			{
				if(iState & (STATE_PRERECV|STATE_REQSENT|STATE_OPERATE|STATE_ACKREQ|STATE_PAUSED))
				{
					char *msg = Translate("Cancelled by remote user");
					SetDlgItemText(hDlg, IDC_STATUS, msg);
					MakePopupMsg(hDlg, hContact, msg);
					destroyTransfer();
					setState(STATE_CANCELLED);
				}
				break;
			}
			case CMD_DACK:
				cmdDACK(str+1);
				break;
		}
	else
		switch(msgId)
		{
			case CMD_CANCEL:
			{
				if(iState & (STATE_PRERECV|STATE_REQSENT|STATE_OPERATE|STATE_ACKREQ|STATE_PAUSED))
				{
					char *msg = Translate("Cancelled by remote user");
					SetDlgItemText(hDlg, IDC_STATUS, msg);
					MakePopupMsg(hDlg, hContact, msg);
					destroyTransfer();
					setState(STATE_CANCELLED);
				}
				break;
			}
			case CMD_REQ:
				if(chunkCount)
				{
					if(MessageBox(hDlg, Translate("New incoming file request. Do you want proceed?"),
						Translate(SERVICE_TITLE), MB_YESNO | MB_ICONWARNING) != IDYES)
						break;
					//sendCmd(0, CMD_CANCEL, "", NULL);
					destroyTransfer();
				}
				SetDlgItemText(hDlg, IDC_STATUS, "");
				incomeRequest(str+1);
				break;
			case CMD_DATA:
				cmdDATA(str+1);
				break;
			case CMD_END:
				cmdEND();
				break;
		};
};

int FILEECHO::sendCmd(int id, int cmd, char *szParam, char *szPrefix)
{
	char *buf;
	int retval;
	int buflen = (int)_tcslen(szServicePrefix) + (int)_tcslen(szParam) + 2;
	if(szPrefix != NULL)
		buflen += (int)_tcslen(szPrefix);
	
	buf = (char*)malloc(buflen);
	if(szPrefix == NULL)
		_snprintf(buf,buflen,"%s%c%s", szServicePrefix, cCmdList[cmd], szParam);
	else
		_snprintf(buf,buflen,"%s%c%s%s", szServicePrefix, cCmdList[cmd], szPrefix, szParam);
	retval = CallContactService(hContact, PSS_MESSAGE, 0, (LPARAM)buf);
	free(buf);
	updateProgress();
	return retval;	
}


void CreateDirectoryTree(char *szDir)
{
	DWORD dwAttributes;
	char *pszLastBackslash,szTestDir[MAX_PATH];

	lstrcpyn(szTestDir,szDir,sizeof(szTestDir));
	if((dwAttributes=GetFileAttributes(szTestDir))!=0xffffffff
	   && dwAttributes&FILE_ATTRIBUTE_DIRECTORY) return;
	pszLastBackslash=strrchr(szTestDir,'\\');
	if(pszLastBackslash==NULL) {GetCurrentDirectory(MAX_PATH,szDir); return;}
	*pszLastBackslash='\0';
	CreateDirectoryTree(szTestDir);
	CreateDirectory(szTestDir,NULL);
}

void SubclassWnd(HWND hwnd, WNDPROC lpfnWndProc)
{
	SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
	SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG)lpfnWndProc);
}
#define CallSubclassed(hwnd, uMsg, wParam, lParam)\
	CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, uMsg, wParam, lParam)

LRESULT CALLBACK ProgressWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
		case WM_NCPAINT:
			return 0;
		case WM_PAINT:
		{
			HDC hdc;
			PAINTSTRUCT ps;
			RECT rc;
			HRGN hrgn;
			HBRUSH frameBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
			struct FILEECHO *dat;

			dat = (struct FILEECHO*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
			//if(dat == NULL)
			//	return CallSubclassed(hwnd, uMsg, wParam, lParam);
			GetClientRect(hwnd, &rc);
			if(dat == NULL || dat->chunkCount == 0)
			{
				COLORREF colour;
				HBRUSH hbr;

				if(dat == NULL || dat->iState != STATE_FINISHED)
				{
					hbr = (HBRUSH)(COLOR_3DFACE+1);
				}
				else
				{
					colour = dat->rgbRecv;
					hbr = CreateSolidBrush(colour);
				}
				hdc=BeginPaint(hwnd,&ps);
					FillRect(hdc, &rc, hbr);
					FrameRect(hdc, &rc, frameBrush);
					if(hbr != (HBRUSH)(COLOR_3DFACE+1))
						DeleteObject(hbr);
				EndPaint(hwnd,&ps);
				return 0;
			}

			hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
			
			hdc=BeginPaint(hwnd,&ps);
				SelectClipRgn(hdc, hrgn);
			
				RECT rc2 = rc;
				//uint sliceWidth = (rc.right - rc.left) / dat->chunkCount;
				float sliceWidth = (float)((float)(rc.right - rc.left) / (float)dat->chunkCount);
				float dx = (float)rc2.left;
				for(uint indx = 0; indx < dat->chunkCount; indx++)
				{
					HBRUSH hbr;
					COLORREF colour;
					if(dat->inSend && indx == dat->chunkIndx)
						colour = dat->rgbToSend;
					else
						switch(dat->chunkAck[indx])
						{
							case CHUNK_UNSENT:
								colour = dat->rgbUnSent;
								break;
							case CHUNK_SENT:
								colour = dat->rgbSent;
								break;
							case CHUNK_ACK:
								colour = dat->rgbRecv;
								break;
						}
					/*
					if(indx == 5) colour = RGB(255,64,64);
					else if(indx < 2) colour = RGB(64,255,64);
					else if(indx < 4) colour = RGB(255,255,64);
					else colour = RGB(128,128,128);
					//*/
					if(indx == dat->chunkCount-1)
						rc2.right = rc.right;
					hbr = CreateSolidBrush(colour);
					rc2.left = (int)dx;
					rc2.right = (int)(dx + sliceWidth);
					FillRect(hdc, &rc2, hbr);
					FrameRect(hdc, &rc2, frameBrush);
					DeleteObject(hbr);
					dx += sliceWidth-1;
				}
				if(rc2.right < rc.right)
				{
					rc2.left = rc2.right;
					rc2.right = rc.right;
					FillRect(hdc, &rc2, (HBRUSH)(COLOR_3DFACE+1));
				}
				//FrameRect(hdc, &rc, (HBRUSH)(COLOR_3DLIGHT+1));
				//OffsetRect(&rc, 1,1);
				//FrameRect(hdc, &rc, (HBRUSH)(COLOR_BTNTEXT+1));
				//FrameRect(hdc, &rc, (HBRUSH)(COLOR_BTNTEXT+1));
			EndPaint(hwnd,&ps);

			DeleteObject(hrgn);

			return 0;
		}
	}
	return CallSubclassed(hwnd, uMsg, wParam, lParam);
}

INT_PTR CALLBACK DialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	struct FILEECHO *dat = (struct FILEECHO*)GetWindowLongPtr(hDlg, GWLP_USERDATA);
	switch( uMsg )
	{
		case WM_INITDIALOG:
		{
			dat = (FILEECHO*)lParam;
			dat->hDlg = hDlg;

			dat->updateTitle();

			CreateStatusWindow(WS_CHILD|WS_VISIBLE, "", hDlg, IDC_STATUS);
			SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG)dat);
			WindowList_Add(hFileList, hDlg, dat->hContact);
			SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcons[ICON_MAIN]);
			SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcons[ICON_MAIN]);
			SendDlgItemMessage(hDlg, IDC_STOP, BUTTONADDTOOLTIP,(WPARAM)Translate(hint_controls[ICON_STOP]),0);

			//SetDlgItemText(hDlg, IDC_FILENAME, "C:\\!Developer\\!Miranda\\miranda\\bin\\release\\emo\\biggrin.gif");

			SubclassWnd(GetDlgItem(hDlg, IDC_PROGRESS), ProgressWndProc);
			
			SendDlgItemMessage(hDlg, IDC_PLAY, BUTTONSETASFLATBTN,0,0);				
			SendDlgItemMessage(hDlg, IDC_PLAY, BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[ICON_PLAY]);
			SendDlgItemMessage(hDlg, IDC_STOP, BUTTONSETASFLATBTN,0,0);
			SendDlgItemMessage(hDlg, IDC_STOP, BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[ICON_STOP]);
			dat->setState(STATE_IDLE);

			//ShowWindow(hDlg, SW_HIDE);
			//UpdateWindow(hDlg);

			if(dat->inSend)
				PostMessage(hDlg, WM_COMMAND, IDC_BROWSE, NULL);

			return FALSE;
		}
		case WM_FE_MESSAGE:
		{
			dat->perform((char *)lParam);
			delete (char *)lParam;

			return TRUE;
		}
		case WM_FE_SKINCHANGE:
			SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcons[ICON_MAIN]);
			SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcons[ICON_MAIN]);
			dat->setState(dat->iState);
			SendDlgItemMessage(hDlg, IDC_STOP, BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcons[ICON_STOP]);

			break;
		case WM_FE_STATUSCHANGE:
		{
			char *szProto;
			szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
			if (szProto)
			{
				int dwStatus;
				dwStatus = DBGetContactSettingWord(dat->hContact,szProto,"Status",ID_STATUS_OFFLINE);
				if(dat->inSend && dwStatus != dat->contactStatus)
				{
					if(dat->contactStatus == ID_STATUS_OFFLINE)
					{
						dat->chunkIndx = dat->chunkCount;
					}
					else
						if(dwStatus == ID_STATUS_OFFLINE)
						{
							if(dat->iState & (STATE_OPERATE|STATE_ACKREQ))
							{
								char *msg = Translate("Paused, 'coz connection dropped");
								SetDlgItemText(hDlg, IDC_STATUS, msg);
								MakePopupMsg(dat->hDlg, dat->hContact, msg);
								dat->setState(STATE_PAUSED);
								KillTimer(hDlg, TIMER_SEND);
							}
						}
				}
				dat->contactStatus = dwStatus;
			}
			return TRUE;
		}
		case WM_DESTROY:
			WindowList_Remove(hFileList, hDlg);
			delete dat;
		
			return TRUE;

		case WM_TIMER:
			if(dat->inSend)
				dat->onSendTimer();
			else
				dat->onRecvTimer();
			break;
		case WM_COMMAND:
			switch(wParam)
			{
				case IDC_PLAY:
				{
					if(dat->iState & (STATE_IDLE|STATE_FINISHED|STATE_CANCELLED|STATE_PRERECV))
					{
						int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_FILENAME))+1;
						if(dat->filename) free(dat->filename);
						dat->filename = (char*)malloc(len);
						GetDlgItemText(hDlg, IDC_FILENAME, dat->filename, len);
						if(dat->inSend)
						// Send offer to remote side
						{							
							dat->sendReq();
						}
						else
						// Send the accept and starting to receive
						{
							char buff[MAX_PATH];
							char *bufname;

							GetFullPathName(dat->filename, sizeof(buff), buff, &bufname);
							*bufname = 0;
							CreateDirectoryTree(buff);
							if(!dat->createTransfer())
							{
								SetDlgItemText(hDlg, IDC_STATUS, Translate("Failed on file initialization"));
								break;
							}
							dat->sendCmd(0, CMD_ACCEPT, "");
							dat->lastTimestamp = GetTickCount();
							SetDlgItemText(hDlg, IDC_STATUS, Translate("Receiving..."));
							dat->setState(STATE_OPERATE);
						}
					}
					else
					{
						if(dat->inSend)
						{
							if(dat->iState == STATE_OPERATE)
							{
								SetDlgItemText(hDlg, IDC_STATUS, Translate("Paused..."));
								dat->setState(STATE_PAUSED);
								KillTimer(hDlg, TIMER_SEND);
							}
							else
							{
								SetDlgItemText(hDlg, IDC_STATUS, Translate("Sending..."));
								if(dat->chunkIndx < dat->chunkCount)
									dat->setState(STATE_OPERATE);
								else
									dat->setState(STATE_ACKREQ);
								PostMessage(hDlg, WM_TIMER, 0,0);
								//dat->onRecvTimer();
								//SetTimer(hDlg, TIMER_SEND, dwSendInterval, NULL);
							}
						}
						else
						{
							SetDlgItemText(hDlg, IDC_STATUS, Translate("Synchronizing..."));
							dat->setState(STATE_ACKREQ);
							PostMessage(hDlg, WM_TIMER, 0,0);
							//dat->onRecvTimer();
							//SetTimer(hDlg, TIMER_SEND, dwSendInterval, 0);
						}
						break;
					}
					break;
				}
				case IDC_BROWSE:
				{
					char str[MAX_PATH];
					OPENFILENAME ofn;

					ZeroMemory(&ofn, sizeof(ofn));
					*str = 0;
					GetDlgItemText(hDlg, IDC_FILENAME, str, sizeof(str));
					//ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
					ofn.lStructSize = sizeof(ofn);
					ofn.hwndOwner = hDlg;
					//ofn.lpstrFilter = "*.*";
					ofn.lpstrFile = str;
					ofn.Flags = dat->inSend?OFN_FILEMUSTEXIST:0;
					ofn.lpstrTitle = dat->inSend?Translate("Select a file"):Translate("Save as");
					ofn.nMaxFile = sizeof(str);
					ofn.nMaxFileTitle = MAX_PATH;
					if(!GetOpenFileName(&ofn)) break;
					if(!dat->inSend && dat->iState == STATE_FINISHED) break;
					SetDlgItemText(hDlg, IDC_FILENAME, str);

					int size = RetrieveFileSize(str);
					if(size != -1)
						_snprintf(str, sizeof(str), Translate("Size: %d bytes"), size);
					else
						_snprintf(str, sizeof(str), Translate("Can't get a file size"), size);
					SetDlgItemText(hDlg, IDC_FILESIZE, str);					

					break;
				}

				case IDC_STOP:
				case IDCANCEL:
					if(dat->iState == STATE_PRERECV)
					{
						SetDlgItemText(hDlg, IDC_STATUS, Translate("Cancelled by user"));
						dat->sendCmd(0, CMD_CANCEL, "", NULL);
						dat->setState(STATE_CANCELLED);
					}
					if(dat->chunkCount)
					{
						if(MessageBox(hDlg, Translate("Transfer is in progress. Do you really want to close?"),
							Translate(SERVICE_TITLE), MB_ICONWARNING|MB_YESNO|MB_DEFBUTTON2) == IDYES)
						{
							SetDlgItemText(hDlg, IDC_STATUS, Translate("Cancelled by user"));
							dat->setState(STATE_CANCELLED);
							dat->sendCmd(0, CMD_CANCEL, "", NULL);
							dat->destroyTransfer();
							if(wParam == IDCANCEL)
								DestroyWindow(hDlg);
						}
					}
					else
						if(wParam == IDCANCEL)
							DestroyWindow(hDlg);
					break;//return TRUE;
			}
			break;
	}

	return FALSE;
}