From 3634576b3903f8316b1dcdc396a70f40fa43f5f5 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Wed, 18 Jul 2012 06:59:36 +0000 Subject: ExtraIcon, Favcontacts, FileAsMessage, FingerPrintModPlus: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1006 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/FileAsMessage/src/crc32.cpp | 31 + plugins/FileAsMessage/src/dialog.cpp | 1358 ++++++++++++++++++++++++++++++ plugins/FileAsMessage/src/dialog.h | 120 +++ plugins/FileAsMessage/src/main.cpp | 326 +++++++ plugins/FileAsMessage/src/main.h | 57 ++ plugins/FileAsMessage/src/optionsdlg.cpp | 112 +++ plugins/FileAsMessage/src/resource.h | 45 + 7 files changed, 2049 insertions(+) create mode 100644 plugins/FileAsMessage/src/crc32.cpp create mode 100644 plugins/FileAsMessage/src/dialog.cpp create mode 100644 plugins/FileAsMessage/src/dialog.h create mode 100644 plugins/FileAsMessage/src/main.cpp create mode 100644 plugins/FileAsMessage/src/main.h create mode 100644 plugins/FileAsMessage/src/optionsdlg.cpp create mode 100644 plugins/FileAsMessage/src/resource.h (limited to 'plugins/FileAsMessage/src') diff --git a/plugins/FileAsMessage/src/crc32.cpp b/plugins/FileAsMessage/src/crc32.cpp new file mode 100644 index 0000000000..ab34393f8d --- /dev/null +++ b/plugins/FileAsMessage/src/crc32.cpp @@ -0,0 +1,31 @@ +#include "main.h" + +const ulong CRCPoly = 0xEDB88320; +ulong CRC32Table[256]; + +void InitCRC32() +{ + for (UINT32 i = 0; i < 256; i++) + { + UINT32 r = i; + for (int j = 0; j < 8; j++) + if (r & 1) + r = (r >> 1) ^ CRCPoly; + else + r >>= 1; + CRC32Table[i] = r; + } +} + +const ulong INITCRC = -1L; + +inline ulong UpdateCRC32(uchar val, ulong crc) +{ + return CRC32Table[(uchar)crc^val] ^ (crc>>8); +} + +ulong memcrc32(uchar *ptr, int size, ulong crc ) +{ + while(size--) crc = UpdateCRC32(*ptr++, crc); + return crc; +} diff --git a/plugins/FileAsMessage/src/dialog.cpp b/plugins/FileAsMessage/src/dialog.cpp new file mode 100644 index 0000000000..f038b87a33 --- /dev/null +++ b/plugins/FileAsMessage/src/dialog.cpp @@ -0,0 +1,1358 @@ +#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->cchEncodedcbDecoded)) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + nlb64->cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded); + for(iIn=0,pbIn=nlb64->pbDecoded,pszOut=nlb64->pszEncoded;iIncbDecoded;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->cbDecodedcchEncoded)) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + nlb64->cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded); + for(iIn=0,pszIn=nlb64->pszEncoded,pbOut=nlb64->pbDecoded;iIncchEncoded;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; +} diff --git a/plugins/FileAsMessage/src/dialog.h b/plugins/FileAsMessage/src/dialog.h new file mode 100644 index 0000000000..679742c2f6 --- /dev/null +++ b/plugins/FileAsMessage/src/dialog.h @@ -0,0 +1,120 @@ +#include +#include +#include +extern HINSTANCE hInst; + +int getMsgId(char *msg); + +INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + +#ifdef _DEBUG + #define CHECKPOINT +#else + #define CHECKPOINT error +#endif + +#define TIMER_SEND 100 + +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned long ulong; +enum +{ + CMD_REQ, + CMD_ACCEPT, + CMD_CANCEL, + + CMD_NEXT, + + CMD_DATA, + CMD_DACK, + CMD_END, + + CMD_COUNT +}; + +extern char cFECmd[CMD_COUNT]; + +#define STATE_IDLE 0x100 // idle, no operation +#define STATE_REQSENT 0x02 // request sent, awaiting of response +#define STATE_PRERECV 0x04 // incoming request, awaiting of user +#define STATE_OPERATE 0x08 // operating mode +#define STATE_ACKREQ 0x10 // ACK-request scheduled +#define STATE_CANCELLED 0x20 // operation aborted +#define STATE_FINISHED 0x40 // ... finished successfully +#define STATE_PAUSED 0x80 // ... paused + +#define CHUNK_UNSENT 0x00 +#define CHUNK_SENT 0x01 +#define CHUNK_ACK 0x02 + +#define ICON_PLAY 0 +#define ICON_PAUSE 1 +#define ICON_REFRESH 2 +#define ICON_STOP 3 +#define ICON_MAIN 4 + +struct FILEECHO +{ +public: + HANDLE hContact; + HWND hDlg; + + bool inSend; + int iState; + int contactStatus; + + HANDLE hFile, hMapping; + uchar *lpData; + + char *filename; + uint chunkIndx; // next chunk to send + uint chunkCount; // count of chunks + uint chunkSent; + uint chunkCountx; + uint *chunkPos; // offsets of chunks in file + uchar *chunkAck; // acknowledge of incoming chunks + + uchar codeSymb; // symb for replace NUL-symb. + + uint fileSize; + uint chunkMaxLen; // limit for outgoing chunk + + DWORD lastTimestamp; + DWORD lastDelay; + bool asBinary; + + // settings + uint dwSendInterval; + //uint dwChunkSize; + + COLORREF rgbSent, rgbRecv, rgbUnSent, rgbToSend; +#ifdef DEBUG + uint overhead; +#endif + + FILEECHO(HANDLE Contact); + + void setState(DWORD state); + void updateProgress(); + void updateTitle(); + + void perform(char *str); + void cmdACCEPT(); + void cmdDACK(char *data); + void sendReq(); + int sendCmd(int id, int cmd, char *szParam, char *szPrefix = NULL); + + void cmdDATA(char *data); + void cmdEND(); + + int createTransfer(); + void destroyTransfer(); + + void onSendTimer(); + void onRecvTimer(); + void incomeRequest(char *data); + +}; + +void InitCRC32(); diff --git a/plugins/FileAsMessage/src/main.cpp b/plugins/FileAsMessage/src/main.cpp new file mode 100644 index 0000000000..61dfdb512b --- /dev/null +++ b/plugins/FileAsMessage/src/main.cpp @@ -0,0 +1,326 @@ +#include "main.h" + +PLUGININFOEX pluginInfo = +{ + sizeof(PLUGININFOEX), + SERVICE_TITLE, + PLUGIN_MAKE_VERSION( 0,0,2,4 ), + "File tranfer by using the messaging services - as plain text", + "Denis Stanishevskiy // StDenis", + "stdenformiranda(at)fromru(dot)com", + "Copyright (c) 2004, Denis Stanishevskiy", + PLUGIN_URL, + UNICODE_AWARE, + // {34B5A402-1B79-4246-B041-43D0B590AE2C} + { 0x34b5a402, 0x1b79, 0x4246, { 0xb0, 0x41, 0x43, 0xd0, 0xb5, 0x90, 0xae, 0x2c } } +}; + +HANDLE hFileList; +HINSTANCE hInst; +int hLangpack; + +char *szServiceTitle = SERVICE_TITLE; +char *szServicePrefix = SERVICE_PREFIX; +HANDLE hHookDbSettingChange, hHookContactAdded, hHookSkinIconsChanged; + +extern INT_PTR CALLBACK OptionsDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + +int idIcons[5] = {IDI_PLAY, IDI_PAUSE, IDI_REFRESH, IDI_STOP, IDI_SMALLICON}; +HICON hIcons[5]; + +char *szIconId[5] = +{ + "FePlay", + "FePause", + "FeRefresh", + "FeStop", + "FeMain" +}; +char *szIconName[5] = +{ + "Play", + "Pause", + "Revive", + "Stop", + "Main" +}; +/* +char *szIconGroup[5] = +{ + "gr1", + "gr3", + "gr2", + "gr3", + "gr1" +}; +*/ +int iIconId[5] = {3,2,4,1,0}; + +// +// wParam - Section name +// lParam - Icon ID +// +int OnSkinIconsChanged(WPARAM wParam,LPARAM lParam) +{ + int indx; +/* + if(lParam == NULL) + return 0; + for(indx = 0; indx < ARRAY_SIZE(hIcons); indx++) + { + if(strcmp((char*)lParam, szIconId[indx]) == 0) + { + hIcons[indx] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)szIconId[indx]); + break; + } + } +*/ + for(indx = 0; indx < SIZEOF(hIcons); indx++) + hIcons[indx] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)szIconId[indx]); + + WindowList_Broadcast(hFileList, WM_FE_SKINCHANGE, 0,0); + + return 0; +} + +int OnSettingChanged(WPARAM wParam,LPARAM lParam) +{ + DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam; + + HWND hwnd = WindowList_Find(hFileList,(HANDLE)wParam); + PostMessage(hwnd, WM_FE_STATUSCHANGE, 0,0); + //OnSkinIconsChanged(0,0); + //PostMessage(hwnd, WM_FE_SKINCHANGE, 0,0); + + return 0; +} + +int OnContactAdded(WPARAM wParam,LPARAM lParam) +{ + CallService(MS_PROTO_ADDTOCONTACT, wParam, (LPARAM)SERVICE_NAME); + return 0; +} + +INT_PTR OnRecvFile(WPARAM wParam, LPARAM lParam) +{ + CLISTEVENT *clev = (CLISTEVENT*)lParam; + + HWND hwnd = WindowList_Find(hFileList,(HANDLE)clev->hContact); + if(IsWindow(hwnd)) + { + ShowWindow(hwnd, SW_SHOWNORMAL); + SetForegroundWindow(hwnd); + SetFocus(hwnd); + } + /* + else + { + if(hwnd != 0) WindowList_Remove(hFileList, hwnd); + FILEECHO *fe = new FILEECHO((HANDLE)clev->hContact); + fe->inSend = FALSE; + hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)DialogProc, (LPARAM)fe); + if(hwnd == NULL) + { + delete fe; + return 0; + } + //SendMessage(hwnd, WM_FE_SERVICE, 0, TRUE); + ShowWindow(hwnd, SW_SHOWNORMAL); + } + */ + return 1; +} + +INT_PTR OnSendFile(WPARAM wParam, LPARAM lParam) +{ + HWND hwnd = WindowList_Find(hFileList,(HANDLE)wParam); + if(IsWindow(hwnd)) + { + SetForegroundWindow(hwnd); + SetFocus(hwnd); + } + else + { + if(hwnd != 0) WindowList_Remove(hFileList, hwnd); + FILEECHO *fe = new FILEECHO((HANDLE)wParam); + fe->inSend = TRUE; + hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, (LPARAM)fe); + if(hwnd == NULL) + { + delete fe; + return 0; + } + //SendMessage(hwnd, WM_FE_SERVICE, 0, TRUE); + ShowWindow(hwnd, SW_SHOWNORMAL); + } + return 1; +} + +INT_PTR OnRecvMessage( WPARAM wParam, LPARAM lParam ) +{ + CCSDATA *pccsd = (CCSDATA *)lParam; + PROTORECVEVENT *ppre = ( PROTORECVEVENT * )pccsd->lParam; + + if(strncmp(ppre->szMessage, szServicePrefix, strlen(szServicePrefix))) + return CallService( MS_PROTO_CHAINRECV, wParam, lParam ); + + HWND hwnd = WindowList_Find(hFileList, (HANDLE)pccsd->hContact); + if(!IsWindow(hwnd)) + { + if(hwnd != 0) WindowList_Remove(hFileList, hwnd); + FILEECHO *fe = new FILEECHO((HANDLE)pccsd->hContact); + fe->inSend = FALSE; + hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, (LPARAM)fe); + if(hwnd == NULL) + { + delete fe; + return 0; + } + } + char *msg = strdup(ppre->szMessage + strlen(szServicePrefix)); + PostMessage(hwnd, WM_FE_MESSAGE, (WPARAM)pccsd->hContact, (LPARAM)msg); + + return 0; +} + +int OnOptInitialise(WPARAM wParam, LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp; + + ZeroMemory(&odp, sizeof(odp)); + + odp.cbSize = sizeof(odp); + odp.hInstance = hInst; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS); + odp.ptszTitle = _T(SERVICE_TITLE); + odp.ptszGroup = _T("Plugins"); + odp.flags = ODPF_BOLDGROUPS|ODPF_TCHAR; + odp.pfnDlgProc = OptionsDlgProc; + Options_AddPage(wParam, &odp); + + return 0; +} + +// +// MirandaPluginInfo() +// Called by Miranda to get Version +// +extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD dwVersion) +{ + return &pluginInfo; +} +/* +DWORD CreateSetting(char *name, DWORD defvalue) +{ + if(DBGetContactSettingDword(NULL, SERVICE_NAME, name, -1) == -1) + DBWriteContactSettingDword(NULL, SERVICE_NAME, name, defvalue); + else + defvalue = DBGetContactSettingDword(NULL, SERVICE_NAME, name, defvalue); + return defvalue; +} +*/ + +int OnModulesLoaded(WPARAM wparam,LPARAM lparam) +{ + int indx; + SKINICONDESC sid; + char ModuleName[MAX_PATH]; + + ZeroMemory(&sid, sizeof(sid)); + sid.cbSize = sizeof(sid); + sid.pszSection = Translate("fileAsMessage"); + GetModuleFileName(hInst, ModuleName, sizeof(ModuleName)); + for(indx = 0; indx < SIZEOF(hIcons); indx++) + { + //sid.pszSection = szIconGroup[indx]; + sid.pszName = szIconId[indx]; + sid.pszDescription = szIconName[indx]; + sid.pszDefaultFile = ModuleName; + sid.iDefaultIndex = iIconId[indx]; + Skin_AddIcon(&sid); + } + for(indx = 0; indx < SIZEOF(hIcons); indx++) + hIcons[indx] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)szIconId[indx]); + + hHookSkinIconsChanged = HookEvent(ME_SKIN2_ICONSCHANGED, OnSkinIconsChanged); + HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while(hContact) + { + if(!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)SERVICE_NAME)) + CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)SERVICE_NAME); + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + + CLISTMENUITEM mi; + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(CLISTMENUITEM); + mi.position = 200011; + mi.hIcon = hIcons[ICON_MAIN]; + mi.pszName = Translate("File As Message..."); + mi.pszService = SERVICE_NAME "/FESendFile"; + mi.pszContactOwner = NULL; + mi.flags = CMIF_NOTOFFLINE; + Menu_AddContactMenuItem(&mi); + + return 0; +} + +// +// Startup initializing +// +extern "C" __declspec(dllexport) int Load(void) +{ + mir_getLP(&pluginInfo); + + InitCRC32(); + +// for(int indx = 0; indx < ARRAY_SIZE(hIcons); indx++) +// hIcons[indx] = (HICON)LoadImage(hInst,MAKEINTRESOURCE(idIcons[indx]),IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0); + + hFileList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0); + + //CreateServiceFunction( SERVICE_NAME PS_GETCAPS, FEGetCaps ); + CreateServiceFunction(SERVICE_NAME PSR_MESSAGE, OnRecvMessage); + CreateServiceFunction(SERVICE_NAME "/FESendFile", OnSendFile); + CreateServiceFunction(SERVICE_NAME "/FERecvFile", OnRecvFile); + + PROTOCOLDESCRIPTOR pd; + memset(&pd, 0, sizeof( PROTOCOLDESCRIPTOR)); + pd.cbSize = sizeof(PROTOCOLDESCRIPTOR); + pd.szName = SERVICE_NAME; + pd.type = PROTOTYPE_FILTER; + CallService(MS_PROTO_REGISTERMODULE, 0, ( LPARAM ) &pd); + + HookEvent(ME_OPT_INITIALISE, OnOptInitialise); + HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); + hHookDbSettingChange = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnSettingChanged); + hHookContactAdded = HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded); + hHookSkinIconsChanged = NULL; + + return 0; +} + +// +// Unload() +// Called by Miranda when Plugin is unloaded. +// +extern "C" __declspec(dllexport) int Unload(void) +{ +// if(hFileList) +// WindowList_Broadcast(hFileList, WM_CLOSE, 0,0); + if(hHookSkinIconsChanged != NULL) + UnhookEvent(hHookSkinIconsChanged); + UnhookEvent(hHookDbSettingChange); + UnhookEvent(hHookContactAdded); + + return 0; +} + +// +// DllMain() +// +int WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) +{ + hInst = hInstance; + return 1; +} diff --git a/plugins/FileAsMessage/src/main.h b/plugins/FileAsMessage/src/main.h new file mode 100644 index 0000000000..e21472e5a1 --- /dev/null +++ b/plugins/FileAsMessage/src/main.h @@ -0,0 +1,57 @@ +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#define _WIN32_WINNT 0x0501 + +#include +#include +#include +#include + +#include "newpluginapi.h" +#include "m_system.h" +#include "m_database.h" +#include "m_protomod.h" +#include "m_protosvc.h" +#include "m_langpack.h" +#include "m_clist.h" +#include "m_options.h" +#include "m_clui.h" +#include "m_clc.h" +#include "m_utils.h" +#include "m_skin.h" +#include "m_popup.h" +#include "m_icolib.h" +#include "m_message.h" +#include "m_button.h" +#include "m_netlib.h" +#include "m_file.h" +#include "win2k.h" + +#include "dialog.h" +#include "resource.h" + +#define MAXBUFSIZE 4096 +#define SERVICE_TITLE "File As Message" +#define SERVICE_NAME "FileAsMessage" + +#define SERVICE_PREFIX "<%fAM-0023%>" + +#define PLUGIN_URL "http://miranda-im.org/download/details.php?action=viewfile&id=1811" +#define NOPLUGIN_MESSAGE "\nIf you see this \"garbage\", probably you "\ + "have no \"fileAsMessage\" plugin installed, see "\ + PLUGIN_URL " for more information and download." +extern char *szServiceTitle; +extern char *szServicePrefix; +extern const ulong INITCRC; + +#define WM_FE_MESSAGE WM_USER+100 +#define WM_FE_STATUSCHANGE WM_USER+101 +#define WM_FE_SKINCHANGE WM_USER+102 + +extern HINSTANCE hInst; +extern HANDLE hFileList; +extern HANDLE hEventNewFile; + +extern HICON hIcons[5]; + +ulong memcrc32(uchar *ptr, int size, ulong crc ); \ No newline at end of file diff --git a/plugins/FileAsMessage/src/optionsdlg.cpp b/plugins/FileAsMessage/src/optionsdlg.cpp new file mode 100644 index 0000000000..748a118db5 --- /dev/null +++ b/plugins/FileAsMessage/src/optionsdlg.cpp @@ -0,0 +1,112 @@ +#include "main.h" + +DWORD settingDefault[] = +{ + RGB(64,255,64), + RGB(255,255,64), + RGB(128,128,128), + RGB(192,192,192), + + 6000, + 5000 +}; +char *settingName[] = +{ + "colorRecv", + "colorSent", + "colorUnsent", + "colorTosend", + + "SendDelay", + "ChunkSize" +}; +int settingId[] = +{ + IDC_RECV, + IDC_SENT, + IDC_UNSENT, + IDC_TOSEND, + + -IDC_SENDDELAY, + -IDC_CHUNKSIZE +}; +// +// OptionsDlgProc() +// this handles the options page +// verwaltet die Optionsseite +// +INT_PTR CALLBACK OptionsDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + { + TranslateDialogDefault(hwndDlg); + + for(int indx = 0; indx < SIZEOF(settingId); indx++) + if(settingId[indx] > 0) + SendDlgItemMessage(hwndDlg, settingId[indx], CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, SERVICE_NAME, settingName[indx], settingDefault[indx])); + else + SetDlgItemInt(hwndDlg, -settingId[indx], DBGetContactSettingDword(NULL, SERVICE_NAME, settingName[indx], settingDefault[indx]), FALSE); + + CheckDlgButton(hwndDlg, IDC_ALPHANUM, DBGetContactSettingDword(NULL, SERVICE_NAME, "base64", 1)?BST_CHECKED:BST_UNCHECKED); + + return TRUE; + + } + case WM_COMMAND: + { + if(//MAKEWPARAM(IDC_AUTO, BN_CLICKED) != wParam || + MAKEWPARAM(IDC_ALPHANUM, BN_CLICKED) != wParam) + { + for(int indx = 0; indx < SIZEOF(settingId); indx++) + { + if(LOWORD(wParam) == abs(settingId[indx])) + { + if(settingId[indx] > 0) + { + if(HIWORD(wParam) != CPN_COLOURCHANGED) return FALSE; + } + else + { + if(HIWORD(wParam) != EN_CHANGE) return FALSE; + if((HWND)lParam != GetFocus()) return FALSE; + } + } + } + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0,0); + break; + } + case WM_DESTROY: + return FALSE; + + case WM_NOTIFY: + { + if((((NMHDR*)lParam)->idFrom == 0) && (((LPNMHDR)lParam)->code == PSN_APPLY)) + { + int value; + BOOL succ; + + for(int indx = 0; indx < SIZEOF(settingId); indx++) + { + if(settingId[indx] > 0) + value = SendDlgItemMessage(hwndDlg, settingId[indx], CPM_GETCOLOUR, 0, 0); + else + { + value = GetDlgItemInt(hwndDlg, -settingId[indx], &succ, FALSE); + if(!succ) value = settingDefault[indx]; + } + DBWriteContactSettingDword(NULL,SERVICE_NAME,settingName[indx], value); + } + DBWriteContactSettingDword(NULL,SERVICE_NAME, "base64", (IsDlgButtonChecked(hwndDlg, IDC_ALPHANUM) == BST_CHECKED)?1:0); + + return TRUE; + } + break; + } + + } + + return FALSE; +} diff --git a/plugins/FileAsMessage/src/resource.h b/plugins/FileAsMessage/src/resource.h new file mode 100644 index 0000000000..b6a254dcf7 --- /dev/null +++ b/plugins/FileAsMessage/src/resource.h @@ -0,0 +1,45 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by resources.rc +// +#define IDC_PLAY 1 +#define IDD_DIALOG1 101 +#define IDD_MAIN 101 +#define IDI_SMALLICON 102 +#define IDD_OPTIONS 103 +#define IDI_STOP 104 +#define IDI_PAUSE 105 +#define IDI_PLAY 106 +#define IDI_REFRESH 107 +#define IDC_FILENAME 1001 +#define IDC_BROWSE 1002 +#define IDC_STOP 1003 +#define IDC_REFRESH 1004 +#define IDC_PROGRESS 1005 +#define IDC_STATUS 1006 +#define IDC_FILESIZE 1008 +#define IDC_LIST1 1020 +#define IDC_ALPHANUM 1022 +#define IDC_HIDEDELAY 1051 +#define IDC_CHUNKSIZE 1051 +#define IDC_SHOWDELAY 1052 +#define IDC_SENDDELAY 1052 +#define IDC_SENTCOLOR 1053 +#define IDC_UNSENT 1053 +#define IDC_SENTCOLOR2 1054 +#define IDC_SENT 1054 +#define IDC_SENTCOLOR3 1055 +#define IDC_TOSEND 1055 +#define IDC_SENTCOLOR4 1056 +#define IDC_RECV 1056 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1022 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif -- cgit v1.2.3