/* Avatar History Plugin --------- This plugin uses the event provided by Avatar Service to automatically back up contacts' avatars when they change. Copyright (C) 2006 Matthew Wild - Email: mwild1@gmail.com 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "AvatarHistory.h" BOOL ProtocolEnabled(const char *proto) { if (proto == NULL) return FALSE; char setting[256]; mir_snprintf(setting, sizeof(setting), "%sEnabled", proto); return (BOOL) db_get_b(NULL, MODULE_NAME, setting, TRUE); } BOOL ContactEnabled(HANDLE hContact, char *setting, int def) { if (hContact == NULL) return FALSE; char *proto = GetContactProto(hContact); if (!ProtocolEnabled(proto)) return FALSE; BYTE globpref = db_get_b(NULL, MODULE_NAME, setting, def); BYTE userpref = db_get_b(hContact, MODULE_NAME, setting, BST_INDETERMINATE); return (globpref && userpref == BST_INDETERMINATE) || userpref == BST_CHECKED; } // Returns true if the unicode buffer only contains 7-bit characters. BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize) { for (int nIndex = 0; nIndex < nSize; nIndex++) if (pBuffer[nIndex] > 0x7F) return FALSE; return TRUE; } void ConvertToFilename(TCHAR *str, size_t size) { for(size_t i = 0; i < size && str[i] != '\0'; i++) { switch(str[i]) { case '/': case '\\': case ':': case '*': case '?': case '"': case '<': case '>': case '|': str[i] = '_'; } } } void ErrorExit(HANDLE hContact,LPTSTR lpszFunction) { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the error message and exit the process lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); mir_sntprintf((TCHAR*)lpDisplayBuf, lstrlen((TCHAR*)lpDisplayBuf), _T("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); ShowDebugPopup(hContact,TEXT("Error"), (LPCTSTR)lpDisplayBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); } int GetUIDFromHContact(HANDLE contact, TCHAR* uinout, int uinout_len) { bool found = true; CONTACTINFO cinfo = { sizeof(cinfo) }; cinfo.hContact = contact; cinfo.dwFlag = CNF_UNIQUEID | CNF_TCHAR; if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&cinfo) == 0) { if (cinfo.type == CNFT_ASCIIZ) { lstrcpyn(uinout, cinfo.pszVal, uinout_len); // It is up to us to free the string // The catch? We need to use Miranda's free(), not our CRT's :) mir_free(cinfo.pszVal); } else if (cinfo.type == CNFT_DWORD) _itot(cinfo.dVal,uinout,10); else if (cinfo.type == CNFT_WORD) _itot(cinfo.wVal,uinout,10); else found = false; } else found = false; if (!found) lstrcpyn(uinout, TranslateT("Unknown UIN"), uinout_len); return 0; } TCHAR * GetExtension(TCHAR *file) { if (file == NULL) return _T(""); TCHAR *ext = _tcsrchr(file, _T('.')); if (ext != NULL) ext++; else ext = _T(""); return ext; } TCHAR* GetHistoryFolder(TCHAR *fn) { if (fn == NULL) return NULL; FoldersGetCustomPathT(hFolder, fn, MAX_PATH, basedir); if (!CreateDirectory(fn, NULL)) ErrorExit(NULL,_T("GetHistoryFolder")); return fn; } TCHAR* GetProtocolFolder(TCHAR *fn, char *proto) { GetHistoryFolder(fn); if (proto == NULL) proto = Translate("Unknown Protocol"); mir_sntprintf(fn, MAX_PATH, _T("%s\\%S"), fn, proto); if (!CreateDirectory(fn, NULL)) ErrorExit(NULL,_T("CreateDirectory")); return fn; } TCHAR* GetContactFolder(TCHAR *fn, HANDLE hContact) { TCHAR uin[MAX_PATH]; char *proto = GetContactProto(hContact); GetProtocolFolder(fn, proto); GetUIDFromHContact(hContact, uin, SIZEOF(uin)); mir_sntprintf(fn, MAX_PATH, _T("%s\\%s"), fn, uin); if (!CreateDirectory(fn, NULL)) ErrorExit(hContact,_T("CreateDirectory")); ConvertToFilename(uin, sizeof(uin)); //added so that weather id's like "yw/CI0000" work #ifdef DBGPOPUPS TCHAR log[1024]; mir_sntprintf(log, MAX_REGS(log), _T("Path: %s\nProto: %S\nUIN: %s"), fn, proto, uin); ShowPopup(hContact, _T("AVH Debug: GetContactFolder"), log); #endif return fn; } TCHAR* GetOldStyleAvatarName(TCHAR *fn, HANDLE hContact) { GetContactFolder(fn, hContact); SYSTEMTIME curtime; GetLocalTime(&curtime); mir_sntprintf(fn, MAX_PATH, _T("%s\\%04d-%02d-%02d %02dh%02dm%02ds"), fn, curtime.wYear, curtime.wMonth, curtime.wDay, curtime.wHour, curtime.wMinute, curtime.wSecond); ShowDebugPopup(hContact,TranslateT("AVH Debug: GetOldStyleAvatarName"),fn); return fn; } void CreateOldStyleShortcut(HANDLE hContact, TCHAR *history_filename) { TCHAR shortcut[MAX_PATH] = _T(""); GetOldStyleAvatarName(shortcut, hContact); mir_sntprintf(shortcut, SIZEOF(shortcut), _T("%s.%s.lnk"), shortcut, GetExtension(history_filename)); if (!CreateShortcut(history_filename, shortcut)) { ShowPopup(hContact, TranslateT("Avatar History: Unable to create shortcut"), shortcut); } else { ShowDebugPopup(hContact, TranslateT("AVH Debug: Shortcut created successfully"), shortcut); } } BOOL CopyImageFile(TCHAR *old_file, TCHAR *new_file) { TCHAR *ext = GetExtension(old_file); mir_sntprintf(new_file, MAX_PATH, _T("%s.%s"), new_file, ext); BOOL ret = CopyFile(old_file, new_file, TRUE); if (!ret) ErrorExit(NULL,_T("CopyImageFile")); return !ret; } TCHAR * GetCachedAvatar(char *proto, TCHAR *hash) { TCHAR *ret = NULL; TCHAR file[1024] = _T(""); TCHAR search[1024] = _T(""); if (opts.log_keep_same_folder) GetHistoryFolder(file); else GetProtocolFolder(file, proto); mir_sntprintf(search, SIZEOF(search), _T("%s\\%s.*"), file, hash); WIN32_FIND_DATA finddata; HANDLE hFind = FindFirstFile(search, &finddata); if (hFind == INVALID_HANDLE_VALUE) return NULL; do { size_t len = lstrlen(finddata.cFileName); if (len > 4 && (!lstrcmpi(&finddata.cFileName[len-4], _T(".png")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".bmp")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".gif")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".jpg")) || !lstrcmpi(&finddata.cFileName[len-5], _T(".jpeg")))) { mir_sntprintf(file, SIZEOF(file), _T("%s\\%s"), file, finddata.cFileName); ret = mir_tstrdup(file); break; } } while(FindNextFile(hFind, &finddata)); FindClose(hFind); return ret; } BOOL CreateShortcut(TCHAR *file, TCHAR *shortcut) { IShellLink *psl = NULL; HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &psl); if (SUCCEEDED(hr)) { psl->SetPath(file); IPersistFile *ppf = NULL; hr = psl->QueryInterface(IID_IPersistFile, (void **) &ppf); if (SUCCEEDED(hr)) { hr = ppf->Save(shortcut, TRUE); ppf->Release(); } psl->Release(); } if (FAILED(hr)) ErrorExit(NULL,_T("CreateShortcut")); return SUCCEEDED(hr); } BOOL ResolveShortcut(TCHAR *shortcut, TCHAR *file) { IShellLink* psl = NULL; HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &psl); if (SUCCEEDED(hr)) { IPersistFile* ppf = NULL; hr = psl->QueryInterface(IID_IPersistFile, (void **) &ppf); if (SUCCEEDED(hr)) { hr = ppf->Load(shortcut, STGM_READ); if (SUCCEEDED(hr)) { hr = psl->Resolve(NULL, SLR_UPDATE); if (SUCCEEDED(hr)) { WIN32_FIND_DATA wfd; hr = psl->GetPath(file, MAX_PATH, &wfd, SLGP_RAWPATH); } } ppf->Release(); } psl->Release(); } if (FAILED(hr)) ErrorExit(NULL,_T("CreateShortcut")); return SUCCEEDED(hr); }