/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-12 Miranda IM, 2012-13 Miranda NG project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "commonheaders.h" #include "file.h" TCHAR* PFTS_StringToTchar(int flags, const PROTOCHAR* s); int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS* ft, const PROTOCHAR* s, TCHAR *r); static HANDLE hSRFileMenuItem; TCHAR *GetContactID(HANDLE hContact) { TCHAR *theValue = {0}; char *szProto = GetContactProto(hContact); if (DBGetContactSettingByte(hContact, szProto, "ChatRoom", 0) == 1) { DBVARIANT dbv; if ( !DBGetContactSettingTString(hContact, szProto, "ChatRoomID", &dbv)) { theValue = (TCHAR *)mir_tstrdup(dbv.ptszVal); DBFreeVariant(&dbv); return theValue; } } else { CONTACTINFO ci = {0}; ci.cbSize = sizeof(ci); ci.hContact = hContact; ci.szProto = szProto; ci.dwFlag = CNF_UNIQUEID | CNF_TCHAR; if ( !CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) { switch (ci.type) { case CNFT_ASCIIZ: return (TCHAR *)ci.pszVal; break; case CNFT_DWORD: return _itot(ci.dVal, (TCHAR *)mir_alloc(sizeof(TCHAR)*32), 10); break; } } } return NULL; } static INT_PTR SendFileCommand(WPARAM wParam, LPARAM) { struct FileSendData fsd; fsd.hContact = (HANDLE)wParam; fsd.ppFiles = NULL; CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILESEND), NULL, DlgProcSendFile, (LPARAM)&fsd); return 0; } static INT_PTR SendSpecificFiles(WPARAM wParam, LPARAM lParam) { FileSendData fsd; fsd.hContact = (HANDLE)wParam; char** ppFiles = (char**)lParam; int count = 0; while (ppFiles[count] != NULL) count++; fsd.ppFiles = (const TCHAR**)alloca((count+1) * sizeof(void*)); for (int i=0; i < count; i++) fsd.ppFiles[i] = (const TCHAR*)mir_a2t(ppFiles[i]); fsd.ppFiles[ count ] = NULL; CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILESEND), NULL, DlgProcSendFile, (LPARAM)&fsd); for (int j = 0; j < count; j++) mir_free((void*)fsd.ppFiles[j]); return 0; } static INT_PTR SendSpecificFilesT(WPARAM wParam, LPARAM lParam) { FileSendData fsd; fsd.hContact = (HANDLE)wParam; fsd.ppFiles = (const TCHAR**)lParam; CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILESEND), NULL, DlgProcSendFile, (LPARAM)&fsd); return 0; } static INT_PTR GetReceivedFilesFolder(WPARAM wParam, LPARAM lParam) { TCHAR buf[MAX_PATH]; GetContactReceivedFilesDir((HANDLE)wParam, buf, MAX_PATH, TRUE); char* dir = mir_t2a(buf); lstrcpynA((char*)lParam, dir, MAX_PATH); mir_free(dir); return 0; } static INT_PTR RecvFileCommand(WPARAM, LPARAM lParam) { CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILERECV), NULL, DlgProcRecvFile, lParam); return 0; } void PushFileEvent(HANDLE hContact, HANDLE hdbe, LPARAM lParam) { CLISTEVENT cle = {0}; cle.cbSize = sizeof(cle); cle.hContact = hContact; cle.hDbEvent = hdbe; cle.lParam = lParam; if (DBGetContactSettingByte(NULL, "SRFile", "AutoAccept", 0) && !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0)) { CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILERECV), NULL, DlgProcRecvFile, (LPARAM)&cle); } else { SkinPlaySound("RecvFile"); TCHAR szTooltip[256]; mir_sntprintf(szTooltip, SIZEOF(szTooltip), TranslateT("File from %s"), pcli->pfnGetContactDisplayName(hContact, 0)); cle.ptszTooltip = szTooltip; cle.flags |= CLEF_TCHAR; cle.hIcon = LoadSkinIcon(SKINICON_EVENT_FILE); cle.pszService = "SRFile/RecvFile"; CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle); } } static int FileEventAdded(WPARAM wParam, LPARAM lParam) { DWORD dwSignature; DBEVENTINFO dbei = { sizeof(dbei) }; dbei.cbBlob = sizeof(DWORD); dbei.pBlob = (PBYTE)&dwSignature; db_event_get((HANDLE)lParam, &dbei); if (dbei.flags & (DBEF_SENT|DBEF_READ) || dbei.eventType != EVENTTYPE_FILE || dwSignature == 0) return 0; PushFileEvent((HANDLE)wParam, (HANDLE)lParam, 0); return 0; } int SRFile_GetRegValue(HKEY hKeyBase, const TCHAR *szSubKey, const TCHAR *szValue, TCHAR *szOutput, int cbOutput) { HKEY hKey; DWORD cbOut = cbOutput; if (RegOpenKeyEx(hKeyBase, szSubKey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) return 0; if (RegQueryValueEx(hKey, szValue, NULL, NULL, (PBYTE)szOutput, &cbOut) != ERROR_SUCCESS) { RegCloseKey(hKey); return 0; } RegCloseKey(hKey); return 1; } void GetSensiblyFormattedSize(__int64 size, TCHAR *szOut, int cchOut, int unitsOverride, int appendUnits, int *unitsUsed) { if ( !unitsOverride) { if (size<1000) unitsOverride = UNITS_BYTES; else if (size<100*1024) unitsOverride = UNITS_KBPOINT1; else if (size<1024*1024) unitsOverride = UNITS_KBPOINT0; else if (size<1024*1024*1024) unitsOverride = UNITS_MBPOINT2; else unitsOverride = UNITS_GBPOINT3; } if (unitsUsed) *unitsUsed = unitsOverride; switch(unitsOverride) { case UNITS_BYTES: mir_sntprintf(szOut, cchOut, _T("%u%s%s"), (int)size, appendUnits?_T(" "):_T(""), appendUnits?TranslateT("bytes"):_T("")); break; case UNITS_KBPOINT1: mir_sntprintf(szOut, cchOut, _T("%.1lf%s"), size/1024.0, appendUnits?_T(" KB"):_T("")); break; case UNITS_KBPOINT0: mir_sntprintf(szOut, cchOut, _T("%u%s"), (int)(size/1024), appendUnits?_T(" KB"):_T("")); break; case UNITS_GBPOINT3: mir_sntprintf(szOut, cchOut, _T("%.3f%s"), (size >> 20)/1024.0, appendUnits?_T(" GB"):_T("")); break; default: mir_sntprintf(szOut, cchOut, _T("%.2lf%s"), size/1048576.0, appendUnits?_T(" MB"):_T("")); break; } } // Tripple redirection sucks but is needed to nullify the array pointer void FreeFilesMatrix(TCHAR ***files) { if (*files == NULL) return; // Free each filename in the pointer array TCHAR **pFile = *files; while (*pFile != NULL) { mir_free(*pFile); *pFile = NULL; pFile++; } // Free the array itself mir_free(*files); *files = NULL; } void FreeProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *fts) { mir_free(fts->tszCurrentFile); if (fts->ptszFiles) { for (int i=0;itotalFiles;i++) mir_free(fts->ptszFiles[i]); mir_free(fts->ptszFiles); } mir_free(fts->tszWorkingDir); } void CopyProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest, PROTOFILETRANSFERSTATUS *src) { *dest = *src; if (src->tszCurrentFile) dest->tszCurrentFile = PFTS_StringToTchar(src->flags, src->tszCurrentFile); if (src->ptszFiles) { dest->ptszFiles = (TCHAR**)mir_alloc(sizeof(TCHAR*)*src->totalFiles); for (int i=0; i < src->totalFiles; i++) dest->ptszFiles[i] = PFTS_StringToTchar(src->flags, src->ptszFiles[i]); } if (src->tszWorkingDir) dest->tszWorkingDir = PFTS_StringToTchar(src->flags, src->tszWorkingDir); dest->flags &= ~PFTS_UTF; dest->flags |= PFTS_TCHAR; } void UpdateProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest, PROTOFILETRANSFERSTATUS *src) { dest->hContact = src->hContact; dest->flags = src->flags; if (dest->totalFiles != src->totalFiles) { for (int i=0;itotalFiles;i++) mir_free(dest->ptszFiles[i]); mir_free(dest->ptszFiles); dest->ptszFiles = NULL; dest->totalFiles = src->totalFiles; } if (src->ptszFiles) { if ( !dest->ptszFiles) dest->ptszFiles = (TCHAR**)mir_calloc(sizeof(TCHAR*)*src->totalFiles); for (int i=0; i < src->totalFiles; i++) if ( !dest->ptszFiles[i] || !src->ptszFiles[i] || PFTS_CompareWithTchar(src, src->ptszFiles[i], dest->ptszFiles[i])) { mir_free(dest->ptszFiles[i]); if (src->ptszFiles[i]) dest->ptszFiles[i] = PFTS_StringToTchar(src->flags, src->ptszFiles[i]); else dest->ptszFiles[i] = NULL; } } else if (dest->ptszFiles) { for (int i=0; i < dest->totalFiles; i++) mir_free(dest->ptszFiles[i]); mir_free(dest->ptszFiles); dest->ptszFiles = NULL; } dest->currentFileNumber = src->currentFileNumber; dest->totalBytes = src->totalBytes; dest->totalProgress = src->totalProgress; if (src->tszWorkingDir && ( !dest->tszWorkingDir || PFTS_CompareWithTchar(src, src->tszWorkingDir, dest->tszWorkingDir))) { mir_free(dest->tszWorkingDir); if (src->tszWorkingDir) dest->tszWorkingDir = PFTS_StringToTchar(src->flags, src->tszWorkingDir); else dest->tszWorkingDir = NULL; } if ( !dest->tszCurrentFile || !src->tszCurrentFile || PFTS_CompareWithTchar(src, src->tszCurrentFile, dest->tszCurrentFile)) { mir_free(dest->tszCurrentFile); if (src->tszCurrentFile) dest->tszCurrentFile = PFTS_StringToTchar(src->flags, src->tszCurrentFile); else dest->tszCurrentFile = NULL; } dest->currentFileSize = src->currentFileSize; dest->currentFileProgress = src->currentFileProgress; dest->currentFileTime = src->currentFileTime; dest->flags &= ~PFTS_UTF; dest->flags |= PFTS_TCHAR; } static void RemoveUnreadFileEvents(void) { HANDLE hContact = db_find_first(); while (hContact) { HANDLE hDbEvent = db_event_firstUnread(hContact); while (hDbEvent) { DBEVENTINFO dbei = { sizeof(dbei) }; db_event_get(hDbEvent, &dbei); if ( !(dbei.flags&(DBEF_SENT|DBEF_READ)) && dbei.eventType == EVENTTYPE_FILE) db_event_markRead(hContact, hDbEvent); hDbEvent = db_event_next(hDbEvent); } hContact = db_find_next(hContact); } } static int SRFilePreBuildMenu(WPARAM wParam, LPARAM) { CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIM_FLAGS | CMIF_HIDDEN; char *szProto = GetContactProto((HANDLE)wParam); if (szProto != NULL) { if ( CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_FILESEND) { if ( CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_OFFLINEFILES) mi.flags = CMIM_FLAGS; else if (DBGetContactSettingWord((HANDLE)wParam, szProto, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) mi.flags = CMIM_FLAGS; } } CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hSRFileMenuItem, (LPARAM)&mi); return 0; } static int SRFileModulesLoaded(WPARAM, LPARAM) { CLISTMENUITEM mi = { sizeof(mi) }; mi.position = -2000020000; mi.icolibItem = GetSkinIconHandle(SKINICON_EVENT_FILE); mi.pszName = LPGEN("&File"); mi.pszService = MS_FILE_SENDFILE; mi.flags = CMIF_ICONFROMICOLIB; hSRFileMenuItem = Menu_AddContactMenuItem(&mi); RemoveUnreadFileEvents(); return 0; } INT_PTR FtMgrShowCommand(WPARAM, LPARAM) { FtMgr_Show(true, true); return 0; } INT_PTR openContRecDir(WPARAM wparam, LPARAM) { TCHAR szContRecDir[MAX_PATH]; HANDLE hContact = (HANDLE)wparam; GetContactReceivedFilesDir(hContact, szContRecDir, SIZEOF(szContRecDir), TRUE); ShellExecute(0, _T("open"), szContRecDir, 0, 0, SW_SHOW); return 0; } INT_PTR openRecDir(WPARAM, LPARAM) { TCHAR szContRecDir[MAX_PATH]; GetReceivedFilesDir(szContRecDir, SIZEOF(szContRecDir)); ShellExecute(0, _T("open"), szContRecDir, 0, 0, SW_SHOW); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// static void sttRecvCreateBlob(DBEVENTINFO& dbei, int fileCount, char** pszFiles, char* szDescr) { dbei.cbBlob = sizeof(DWORD); { for (int i=0; i < fileCount; i++) dbei.cbBlob += lstrlenA(pszFiles[i]) + 1; } dbei.cbBlob += lstrlenA(szDescr) + 1; if ((dbei.pBlob = (BYTE*)mir_alloc(dbei.cbBlob)) == 0) return; *(DWORD*)dbei.pBlob = 0; BYTE* p = dbei.pBlob + sizeof(DWORD); for (int i=0; i < fileCount; i++) { strcpy((char*)p, pszFiles[i]); p += lstrlenA(pszFiles[i]) + 1; } strcpy((char*)p, (szDescr == NULL) ? "" : szDescr); } static INT_PTR Proto_RecvFileT(WPARAM, LPARAM lParam) { CCSDATA* ccs = (CCSDATA*)lParam; PROTORECVFILET* pre = (PROTORECVFILET*)ccs->lParam; if (pre->fileCount == 0) return 0; DBEVENTINFO dbei = { 0 }; dbei.cbSize = sizeof(dbei); dbei.szModule = GetContactProto(ccs->hContact); dbei.timestamp = pre->timestamp; dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0; dbei.eventType = EVENTTYPE_FILE; char** pszFiles = (char**)alloca(pre->fileCount * sizeof(char*)); { for (int i=0; i < pre->fileCount; i++) pszFiles[i] = Utf8EncodeT(pre->ptszFiles[i]); } char* szDescr = Utf8EncodeT(pre->tszDescription); dbei.flags |= DBEF_UTF; sttRecvCreateBlob(dbei, pre->fileCount, pszFiles, szDescr); { for (int i=0; i < pre->fileCount; i++) mir_free(pszFiles[i]); } mir_free(szDescr); HANDLE hdbe = db_event_add(ccs->hContact, &dbei); PushFileEvent(ccs->hContact, hdbe, pre->lParam); mir_free(dbei.pBlob); return 0; } int LoadSendRecvFileModule(void) { CreateServiceFunction("FtMgr/Show", FtMgrShowCommand); CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIF_ICONFROMICOLIB; mi.icolibItem = GetSkinIconHandle(SKINICON_EVENT_FILE); mi.position = 1900000000; mi.pszName = LPGEN("File &Transfers..."); mi.pszService = "FtMgr/Show"; //MS_PROTO_SHOWFTMGR; Menu_AddMainMenuItem(&mi); HookEvent(ME_SYSTEM_MODULESLOADED, SRFileModulesLoaded); HookEvent(ME_DB_EVENT_ADDED, FileEventAdded); HookEvent(ME_OPT_INITIALISE, FileOptInitialise); HookEvent(ME_CLIST_PREBUILDCONTACTMENU, SRFilePreBuildMenu); CreateServiceFunction(MS_PROTO_RECVFILET, Proto_RecvFileT); CreateServiceFunction(MS_FILE_SENDFILE, SendFileCommand); CreateServiceFunction(MS_FILE_SENDSPECIFICFILES, SendSpecificFiles); CreateServiceFunction(MS_FILE_SENDSPECIFICFILEST, SendSpecificFilesT); CreateServiceFunction(MS_FILE_GETRECEIVEDFILESFOLDER, GetReceivedFilesFolder); CreateServiceFunction("SRFile/RecvFile", RecvFileCommand); CreateServiceFunction("SRFile/OpenContRecDir", openContRecDir); CreateServiceFunction("SRFile/OpenRecDir", openRecDir); SkinAddNewSoundEx("RecvFile", LPGEN("File"), LPGEN("Incoming")); SkinAddNewSoundEx("FileDone", LPGEN("File"), LPGEN("Complete")); SkinAddNewSoundEx("FileFailed", LPGEN("File"), LPGEN("Error")); SkinAddNewSoundEx("FileDenied", LPGEN("File"), LPGEN("Denied")); return 0; }