#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, 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, 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; }