/* 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 "stdafx.h" CLIST_INTERFACE *pcli; HINSTANCE hInst; DWORD mirVer; HANDLE hFolder = NULL; wchar_t profilePath[MAX_PATH]; // database profile path (read at startup only) wchar_t basedir[MAX_PATH]; int hLangpack = 0; MWindowList hAvatarWindowsList = NULL; int OptInit(WPARAM wParam,LPARAM lParam); wchar_t* GetHistoryFolder(wchar_t *fn); wchar_t* GetProtocolFolder(wchar_t *fn, char *proto); wchar_t* GetOldStyleAvatarName(wchar_t *fn, MCONTACT hContact); void InitMenuItem(); PLUGININFOEX pluginInfo = { sizeof(PLUGININFOEX), __PLUGIN_NAME, PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), __DESCRIPTION, __AUTHOR, __AUTHOREMAIL, __COPYRIGHT, __AUTHORWEB, UNICODE_AWARE, // {DBE8C990-7AA0-458D-BAB7-33EB07238E71} {0xdbe8c990, 0x7aa0, 0x458d, {0xba, 0xb7, 0x33, 0xeb, 0x7, 0x23, 0x8e, 0x71}} }; extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD) { return &pluginInfo; } ///////////////////////////////////////////////////////////////////////////////////////// BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD, LPVOID) { hInst = hinstDLL; return TRUE; } ///////////////////////////////////////////////////////////////////////////////////////// // services static INT_PTR GetCachedAvatar(WPARAM wParam, LPARAM lParam) { wchar_t hash[128]; wcsncpy_s(hash, (wchar_t*)lParam, _TRUNCATE); ConvertToFilename(hash, _countof(hash)); return (INT_PTR)GetCachedAvatar((char*)wParam, hash); } static INT_PTR IsEnabled(WPARAM wParam, LPARAM) { MCONTACT hContact = (MCONTACT) wParam; return ContactEnabled(hContact, "LogToDisk", AVH_DEF_LOGTODISK) || ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) || ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY); } ///////////////////////////////////////////////////////////////////////////////////////// // events // fired when the contacts avatar changes // wParam = hContact // lParam = struct avatarCacheEntry *cacheEntry // the event CAN pass a NULL pointer in lParam which means that the avatar has changed, // but is no longer valid (happens, when a contact removes his avatar, for example). // DONT DESTROY the bitmap handle passed in the struct avatarCacheEntry * // // It is also possible that this event passes 0 as wParam (hContact), in which case, // a protocol picture (pseudo - avatar) has been changed. static int AvatarChanged(WPARAM hContact, LPARAM lParam) { if (hContact == NULL) return 0; char *proto = GetContactProto(hContact); if (proto == NULL) return 0; if (mir_strcmp(META_PROTO, proto) == 0) return 0; DBVARIANT dbvOldHash = {0}; bool ret = (db_get_ws(hContact,MODULE_NAME,"AvatarHash",&dbvOldHash) == 0); CONTACTAVATARCHANGEDNOTIFICATION* avatar = (CONTACTAVATARCHANGEDNOTIFICATION*)lParam; if (avatar == NULL) { if (!ret || !mir_wstrcmp(dbvOldHash.ptszVal, L"-")) { //avoid duplicate "removed avatar" notifications //do not notify on an empty profile ShowDebugPopup(hContact, L"AVH Debug", L"Removed avatar, no avatar before... skipping"); db_free(&dbvOldHash); return 0; } SkinPlaySound("avatar_removed"); // Is a flash avatar or avs could not load it db_set_ws(hContact, MODULE_NAME, "AvatarHash", L"-"); if (ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) && opts.popup_show_removed) ShowPopup(hContact, NULL, opts.popup_removed); } else { if (ret && !mir_wstrcmp(dbvOldHash.ptszVal, avatar->hash)) { // same avatar hash, skipping ShowDebugPopup(hContact, L"AVH Debug", L"Hashes are the same... skipping"); db_free(&dbvOldHash); return 0; } SkinPlaySound("avatar_changed"); db_set_ws(hContact, "AvatarHistory", "AvatarHash", avatar->hash); wchar_t history_filename[MAX_PATH] = L""; if (ContactEnabled(hContact, "LogToDisk", AVH_DEF_LOGTODISK)) { if (!opts.log_store_as_hash) { if (opts.log_per_contact_folders) { GetOldStyleAvatarName(history_filename, hContact); if (CopyImageFile(avatar->filename, history_filename)) ShowPopup(hContact, TranslateT("Avatar history: Unable to save avatar"), history_filename); else ShowDebugPopup(hContact, L"AVH Debug: File copied successfully", history_filename); MCONTACT hMetaContact = db_mc_getMeta(hContact); if (hMetaContact && ContactEnabled(hMetaContact, "LogToDisk", AVH_DEF_LOGTOHISTORY)) { wchar_t filename[MAX_PATH] = L""; GetOldStyleAvatarName(filename, hMetaContact); if (CopyImageFile(avatar->filename, filename)) ShowPopup(hContact, TranslateT("Avatar history: Unable to save avatar"), filename); else ShowDebugPopup(hContact, L"AVH Debug: File copied successfully", filename); } } } else { // See if we already have the avatar wchar_t hash[128]; wcsncpy_s(hash, avatar->hash, _TRUNCATE); ConvertToFilename(hash, _countof(hash)); wchar_t *file = GetCachedAvatar(proto, hash); if (file != NULL) { mir_wstrncpy(history_filename, file, _countof(history_filename)); mir_free(file); } else { if (opts.log_keep_same_folder) GetHistoryFolder(history_filename); else GetProtocolFolder(history_filename, proto); mir_snwprintf(history_filename, L"%s\\%s", history_filename, hash); if (CopyImageFile(avatar->filename, history_filename)) ShowPopup(hContact, TranslateT("Avatar history: Unable to save avatar"), history_filename); else ShowDebugPopup(hContact, L"AVH Debug: File copied successfully", history_filename); } if (opts.log_per_contact_folders) { CreateOldStyleShortcut(hContact, history_filename); MCONTACT hMetaContact = db_mc_getMeta(hContact); if (hMetaContact && ContactEnabled(hMetaContact, "LogToDisk", AVH_DEF_LOGTOHISTORY)) CreateOldStyleShortcut(hMetaContact, history_filename); } } } if (ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) && opts.popup_show_changed) ShowPopup(hContact, NULL, opts.popup_changed); if (ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY)) { wchar_t rel_path[MAX_PATH]; PathToRelativeT(history_filename, rel_path); T2Utf blob(rel_path); DBEVENTINFO dbei = { sizeof(dbei) }; dbei.szModule = GetContactProto(hContact); dbei.flags = DBEF_READ | DBEF_UTF; dbei.timestamp = (DWORD) time(NULL); dbei.eventType = EVENTTYPE_AVATAR_CHANGE; dbei.cbBlob = (DWORD)mir_strlen(blob) + 1; dbei.pBlob = blob; db_event_add(hContact, &dbei); } } return 0; } static int PreShutdown(WPARAM, LPARAM) { WindowList_Broadcast(hAvatarWindowsList,WM_CLOSE,0,0); return 0; } static int ModulesLoaded(WPARAM, LPARAM) { mir_snwprintf(basedir, L"%s\\Avatars History", profilePath); hFolder = FoldersRegisterCustomPathT( LPGEN("Avatars"), LPGEN("Avatar History"), PROFILE_PATHT L"\\" CURRENT_PROFILET L"\\Avatars History"); InitPopups(); HookEvent(ME_AV_CONTACTAVATARCHANGED, AvatarChanged); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// static INT_PTR CALLBACK FirstRunDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM) { switch(uMsg) { case WM_INITDIALOG: SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)createDefaultOverlayedIcon(TRUE)); SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)createDefaultOverlayedIcon(FALSE)); TranslateDialogDefault(hwnd); CheckDlgButton(hwnd, IDC_MIR_PROTO, BST_CHECKED); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: { int ret = 0; if (IsDlgButtonChecked(hwnd, IDC_MIR_SAME)) ret = IDC_MIR_SAME; else if (IsDlgButtonChecked(hwnd, IDC_MIR_PROTO)) ret = IDC_MIR_PROTO; else if (IsDlgButtonChecked(hwnd, IDC_MIR_SHORT)) ret = IDC_MIR_SHORT; else if (IsDlgButtonChecked(hwnd, IDC_SHORT)) ret = IDC_SHORT; else if (IsDlgButtonChecked(hwnd, IDC_DUP)) ret = IDC_DUP; EndDialog(hwnd, ret); return TRUE; } } break; case WM_CLOSE: EndDialog(hwnd, 0); return TRUE; } return FALSE; } extern "C" __declspec(dllexport) int Load(void) { mir_getLP(&pluginInfo); pcli = Clist_GetInterface(); CoInitialize(NULL); // Is first run? if ( db_get_b(NULL, MODULE_NAME, "FirstRun", 1)) { // Show dialog int ret = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_FIRST_RUN), NULL, FirstRunDlgProc, 0); if (ret == 0) return -1; // Write settings db_set_b(NULL, MODULE_NAME, "LogToDisk", 1); if (ret == IDC_MIR_SAME) db_set_b(NULL, MODULE_NAME, "LogKeepSameFolder", 1); else db_set_b(NULL, MODULE_NAME, "LogKeepSameFolder", 0); if (ret == IDC_MIR_SHORT || ret == IDC_SHORT || ret == IDC_DUP) db_set_b(NULL, MODULE_NAME, "LogPerContactFolders", 1); else db_set_b(NULL, MODULE_NAME, "LogPerContactFolders", 0); if (ret == IDC_DUP) db_set_b(NULL, MODULE_NAME, "StoreAsHash", 0); else db_set_b(NULL, MODULE_NAME, "StoreAsHash", 1); if (ret == IDC_MIR_SAME || ret == IDC_MIR_PROTO || ret == IDC_MIR_SHORT) db_set_b(NULL, MODULE_NAME, "LogToHistory", 1); else db_set_b(NULL, MODULE_NAME, "LogToHistory", 0); db_set_b(NULL, MODULE_NAME, "FirstRun", 0); } LoadOptions(); HookEvent(ME_SYSTEM_MODULESLOADED,ModulesLoaded); HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); HookEvent(ME_OPT_INITIALISE, OptInit); HookEvent(ME_SKIN2_ICONSCHANGED, IcoLibIconsChanged); HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu); CreateServiceFunction(MS_AVATARHISTORY_ENABLED, IsEnabled); CreateServiceFunction(MS_AVATARHISTORY_GET_CACHED_AVATAR, GetCachedAvatar); Profile_GetPathW(MAX_PATH, profilePath); SkinAddNewSoundExW("avatar_changed",LPGENW("Avatar history"),LPGENW("Contact changed avatar")); SkinAddNewSoundExW("avatar_removed",LPGENW("Avatar history"),LPGENW("Contact removed avatar")); hAvatarWindowsList = WindowList_Create(); SetupIcoLib(); InitMenuItem(); return 0; } extern "C" __declspec(dllexport) int Unload(void) { WindowList_Destroy(hAvatarWindowsList); return 0; }