/* Paste It plugin Copyright (C) 2011 Krzysztof Kral 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 version 2 of the License. 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, see . */ #include "stdafx.h" PasteToWeb* pasteToWebs[PasteToWeb::pages]; std::map* contactWindows; DWORD gMirandaVersion; HANDLE hModulesLoaded, hTabsrmmButtonPressed; HANDLE g_hNetlibUser; HANDLE hPrebuildContactMenu; HANDLE hServiceContactMenu; HGENMENU hContactMenu; HGENMENU hWebPageMenus[PasteToWeb::pages]; HANDLE hOptionsInit; HANDLE hWindowEvent = NULL; HINSTANCE hInst; #define FROM_CLIPBOARD 10 #define FROM_FILE 11 #define DEF_PAGES_START 20 #define MS_PASTEIT_CONTACTMENU "PasteIt/ContactMenu" PLUGININFOEX pluginInfo={ sizeof(PLUGININFOEX), __PLUGIN_NAME, PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), __DESCRIPTION, __AUTHOR, __AUTHOREMAIL, __COPYRIGHT, __AUTHORWEB, UNICODE_AWARE, // {1AAC15E8-DCEC-4050-B66F-2AA0E6120C22} {0x1aac15e8, 0xdcec, 0x4050, {0xb6, 0x6f, 0x2a, 0xa0, 0xe6, 0x12, 0xc, 0x22}} }; static IconItem icon = { LPGEN("Paste It"), "PasteIt_main", IDI_MENU }; XML_API xi = {0}; int hLangpack = 0; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { hInst = hModule; return TRUE; } extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) { gMirandaVersion = mirandaVersion; if (mirandaVersion < PLUGIN_MAKE_VERSION(0, 8, 0, 0)) return NULL; return &pluginInfo; } std::wstring GetFile() { TCHAR filter[512]; _tcscpy_s(filter, 512, TranslateT("All Files (*.*)")); memcpy(filter + _tcslen(filter), _T("\0*.*\0"), 6 * sizeof(TCHAR)); TCHAR stzFilePath[1024]; stzFilePath[0] = 0; stzFilePath[1] = 0; OPENFILENAME ofn = {0}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = 0; ofn.lpstrFilter = filter; ofn.nFilterIndex = 1; ofn.lpstrFile = stzFilePath; ofn.lpstrTitle = TranslateT("Paste It - Select file"); ofn.nMaxFile = 1024; ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; if(GetOpenFileName(&ofn)) { return stzFilePath; } return L""; } void PasteIt(HANDLE hContact, int mode) { PasteToWeb* pasteToWeb = pasteToWebs[Options::instance->defWeb]; if(mode == FROM_CLIPBOARD) { pasteToWeb->FromClipboard(); } else if(mode == FROM_FILE) { std::wstring file = GetFile(); if(file.length() > 0) { pasteToWeb->FromFile(file); } else return; } else return; if(pasteToWeb->szFileLink[0] == 0 && pasteToWeb->error != NULL) { MessageBox(NULL, pasteToWeb->error, TranslateT("Error"), MB_OK | MB_ICONERROR); } else if(hContact != NULL && pasteToWeb->szFileLink[0] != 0) { char *szProto = GetContactProto(hContact); if (szProto && (INT_PTR)szProto != CALLSERVICE_NOTFOUND) { BOOL isChat = DBGetContactSettingByte(hContact, szProto, "ChatRoom", 0); if(Options::instance->autoSend) { if(!isChat) { DBEVENTINFO dbei = {0}; dbei.cbSize = sizeof(dbei); dbei.eventType = EVENTTYPE_MESSAGE; dbei.flags = DBEF_SENT; dbei.szModule = szProto; dbei.timestamp = (DWORD)time(NULL); dbei.cbBlob = (DWORD)strlen(pasteToWeb->szFileLink) + 1; dbei.pBlob = (PBYTE)pasteToWeb->szFileLink; db_event_add(hContact, &dbei); CallContactService(hContact, PSS_MESSAGE, 0, (LPARAM)pasteToWeb->szFileLink); } else { // PSS_MESSAGE is not compatible with chat rooms // there are no simple method to send text to all users // in chat room. // First I check if protocol is unicode or ascii. BOOL isUnicodePlugin = TRUE; PROTOACCOUNT* protoAc = ProtoGetAccount(szProto); if(protoAc != NULL) { // protoAc->ppro is abstract class, that contains // methods implemented in protocol ddl`s segment. // Method address in vptr table must be converted // to hInstance of protocol dll. PROTO_INTERFACE* protoInt = protoAc->ppro; MEMORY_BASIC_INFORMATION mb; INT_PTR *vptr = *(INT_PTR**)&protoAc->ppro; INT_PTR *vtable = (INT_PTR *)*vptr; if(VirtualQuery((void*)vtable[0], &mb, sizeof(MEMORY_BASIC_INFORMATION))) { typedef PLUGININFOEX * (__cdecl * Miranda_Plugin_InfoEx) ( DWORD mirandaVersion ); HINSTANCE hInst = (HINSTANCE)mb.AllocationBase; // Now I can get PLUGININFOEX from protocol Miranda_Plugin_InfoEx infoEx = (Miranda_Plugin_InfoEx) GetProcAddress(hInst, "MirandaPluginInfoEx"); PLUGININFOEX* pi = NULL; if(infoEx != NULL) { pi = infoEx(gMirandaVersion); } // If PLUGININFOEX flags contains UNICODE_AWARE, // this mean that protocol is unicode. if(pi != NULL && pi->cbSize == sizeof(PLUGININFOEX)) { isUnicodePlugin = pi->flags & UNICODE_AWARE; } } } // Next step is to get all protocol sessions and find // one with correct hContact GC_INFO gci = {0}; GCDEST gcd = {0}; GCEVENT gce = {0}; int cnt = (int)CallService(MS_GC_GETSESSIONCOUNT, 0, (LPARAM)szProto); for (int i = 0; i < cnt ; i++ ) { gci.iItem = i; gci.pszModule = szProto; gci.Flags = BYINDEX | HCONTACT | ID; CallService(MS_GC_GETINFO, 0, (LPARAM)(GC_INFO *) &gci); if (gci.hContact == hContact) { // In this place session was finded, gci.pszID contains // session ID, but it is in unicode or ascii format, // depends on protocol wersion gcd.pszModule = szProto; gcd.iType = GC_EVENT_SENDMESSAGE; gcd.ptszID = gci.pszID; gce.cbSize = sizeof(GCEVENT); gce.pDest = &gcd; gce.bIsMe = TRUE; gce.dwFlags = isUnicodePlugin ? (GCEF_ADDTOLOG | GC_UNICODE) : GCEF_ADDTOLOG; wchar_t* s = NULL; if(isUnicodePlugin) { // If session ID is in unicode, text must be too in unicode s = mir_a2u_cp(pasteToWeb->szFileLink, CP_ACP); gce.ptszText = s; } else { // If session ID is in ascii, text must be too in ascii gce.pszText = pasteToWeb->szFileLink; } gce.time = time(NULL); CallService(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce); if(s != NULL) mir_free(s); break; } } } // Send message to focus window CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0); } else { if(isChat) { // MS_MSG_SENDMESSAGE in incompatible with chat rooms, // because it sends text to IDC_MESSAGE window, // but in chat rooms is only IDC_CHAT_MESSAGE window. // contactWindows map contains all opened hContact // with assaigned to them chat windows. // This map is prepared in ME_MSG_WINDOWEVENT event. std::map::iterator it = contactWindows->find(hContact); if(it != contactWindows->end()) { // it->second is imput window, so now I can send to them // new text. Afterr all is sended MS_MSG_SENDMESSAGE // to focus window. SendMessage(it->second, EM_SETSEL, -1, SendMessage(it->second, WM_GETTEXTLENGTH, 0, 0)); SendMessageA(it->second, EM_REPLACESEL, FALSE, (LPARAM)pasteToWeb->szFileLink); CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)hContact, NULL); } else { // If window do not exist, maybe it is not chat CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)pasteToWeb->szFileLink); } } else { CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)pasteToWeb->szFileLink); } } } } } int TabsrmmButtonPressed(WPARAM wParam, LPARAM lParam) { CustomButtonClickData *cbc = (CustomButtonClickData *)lParam; HANDLE hContact = (HANDLE)wParam; if (!strcmp(cbc->pszModule, MODULE) && cbc->dwButtonId == 1 && hContact) { if (cbc->flags == BBCF_ARROWCLICKED) { HMENU hMenu = CreatePopupMenu(); if (hMenu) { AppendMenu(hMenu, MF_STRING, FROM_CLIPBOARD, TranslateT("Paste from clipboard")); AppendMenu(hMenu, MF_STRING, FROM_FILE, TranslateT("Paste from file")); HMENU hDefMenu = CreatePopupMenu(); for(int i = 0 ; i < PasteToWeb::pages; ++i) { UINT flags = MF_STRING; if(Options::instance->defWeb == i) flags |= MF_CHECKED; AppendMenu(hDefMenu, flags, DEF_PAGES_START + i, pasteToWebs[i]->GetName()); } AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hDefMenu, TranslateT("Default web page")); POINT pt; GetCursorPos(&pt); HWND hwndBtn = WindowFromPoint(pt); if (hwndBtn) { RECT rc; GetWindowRect(hwndBtn, &rc); SetForegroundWindow(cbc->hwndFrom); int selected = TrackPopupMenu(hMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, cbc->hwndFrom, 0); if (selected != 0) { if(selected >= DEF_PAGES_START) { Options::instance->SetDefWeb(selected - DEF_PAGES_START); } else { PasteIt(hContact, selected); } } } DestroyMenu(hDefMenu); DestroyMenu(hMenu); } } else { PasteIt(hContact, FROM_CLIPBOARD); } } return 0; } int PrebuildContactMenu(WPARAM wParam, LPARAM lParam) { bool bIsContact = false; char *szProto = GetContactProto((HANDLE)wParam); if (szProto && (INT_PTR)szProto != CALLSERVICE_NOTFOUND) bIsContact = (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IM) ? true : false; CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIM_FLAGS; if (!bIsContact) mi.flags |= CMIF_HIDDEN; else mi.flags &= ~CMIF_HIDDEN; CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenu, (LPARAM)&mi); return 0; } INT_PTR ContactMenuService(WPARAM wParam, LPARAM lParam) { if(lParam >= DEF_PAGES_START) { Options::instance->SetDefWeb(lParam - DEF_PAGES_START); } else { HANDLE hContact = (HANDLE)wParam; PasteIt(hContact, lParam); } return 0; } void InitMenuItems() { CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB | CMIF_TCHAR; mi.icolibItem = icon.hIcolib; mi.position = 3000090005; mi.ptszName = LPGENT("Paste It"); hContactMenu = Menu_AddContactMenuItem(&mi); memset(&mi, 0, sizeof(mi)); mi.cbSize = sizeof(mi); mi.flags = CMIF_CHILDPOPUP | CMIF_ROOTHANDLE | CMIF_TCHAR; mi.pszService = MS_PASTEIT_CONTACTMENU; mi.hParentMenu = hContactMenu; mi.popupPosition = FROM_CLIPBOARD; mi.ptszName = LPGENT("Paste from clipboard"); Menu_AddContactMenuItem(&mi); mi.popupPosition = FROM_FILE; mi.ptszName = LPGENT("Paste from file"); Menu_AddContactMenuItem(&mi); mi.popupPosition = DEF_PAGES_START - 1; mi.ptszName = LPGENT("Default web page"); HGENMENU hDefWebMenu = Menu_AddContactMenuItem(&mi); CLISTMENUITEM mi2 = { sizeof(mi2) }; mi2.pszService = MS_PASTEIT_CONTACTMENU; mi2.hParentMenu = hDefWebMenu; for(int i = 0 ; i < PasteToWeb::pages; ++i) { mi2.flags = CMIF_CHILDPOPUP | CMIF_ROOTHANDLE | CMIF_TCHAR; if(Options::instance->defWeb == i) mi2.flags |= CMIF_CHECKED; mi2.ptszName = pasteToWebs[i]->GetName(); mi2.popupPosition = mi2.position = DEF_PAGES_START + i; hWebPageMenus[i] = Menu_AddContactMenuItem(&mi2); } hPrebuildContactMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu); } void DefWebPageChanged() { CLISTMENUITEM mi = { sizeof(mi) }; for (int i = 0; i < PasteToWeb::pages; i++) { mi.flags = CMIM_FLAGS; if (Options::instance->defWeb == i) mi.flags |= CMIF_CHECKED; CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hWebPageMenus[i], (LPARAM)&mi); } } void InitTabsrmmButton() { if (ServiceExists(MS_BB_ADDBUTTON)) { BBButton btn = {0}; btn.cbSize = sizeof(btn); btn.dwButtonID = 1; btn.pszModuleName = MODULE; btn.dwDefPos = 110; btn.hIcon = icon.hIcolib; btn.bbbFlags = BBBF_ISARROWBUTTON | BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_CANBEHIDDEN | BBBF_ISCHATBUTTON; btn.ptszTooltip = TranslateT("Paste It"); CallService(MS_BB_ADDBUTTON, 0, (LPARAM)&btn); if(hTabsrmmButtonPressed != NULL) UnhookEvent(hTabsrmmButtonPressed); hTabsrmmButtonPressed = HookEvent(ME_MSG_BUTTONPRESSED, TabsrmmButtonPressed); } } int WindowEvent(WPARAM wParam, MessageWindowEventData* lParam) { if(lParam->uType == MSG_WINDOW_EVT_OPEN) { char *szProto = GetContactProto(lParam->hContact); if (szProto && (INT_PTR)szProto != CALLSERVICE_NOTFOUND) { if(DBGetContactSettingByte(lParam->hContact, szProto, "ChatRoom", 0)) { (*contactWindows)[lParam->hContact] = lParam->hwndInput; } } } else if(lParam->uType == MSG_WINDOW_EVT_CLOSE) { std::map::iterator it = contactWindows->find(lParam->hContact); if(it != contactWindows->end()) { contactWindows->erase(it); } } return 0; } int ModulesLoaded(WPARAM wParam, LPARAM lParam) { InitMenuItems(); InitTabsrmmButton(); hWindowEvent = HookEvent(ME_MSG_WINDOWEVENT, (MIRANDAHOOK)WindowEvent); return 0; } extern "C" int __declspec(dllexport) Load(void) { mir_getXI(&xi); mir_getLP(&pluginInfo); Icon_Register(hInst, LPGEN("Paste It"), &icon, 1); NETLIBUSER nlu = {0}; nlu.cbSize = sizeof(nlu); nlu.flags = NUF_TCHAR | NUF_OUTGOING | NUF_HTTPCONNS; nlu.szSettingsModule = MODULE; nlu.ptszDescriptiveName = TranslateT("Paste It HTTP connections"); g_hNetlibUser = ( HANDLE )CallService( MS_NETLIB_REGISTERUSER, 0, ( LPARAM )&nlu ); pasteToWebs[0] = new PasteToWeb1(); pasteToWebs[0]->pageIndex = 0; pasteToWebs[1] = new PasteToWeb2(); pasteToWebs[1]->pageIndex = 1; Options::instance = new Options(); pasteToWebs[0]->ConfigureSettings(); pasteToWebs[1]->ConfigureSettings(); Options::instance->Load(); hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); hOptionsInit = HookEvent(ME_OPT_INITIALISE, Options::InitOptions); hTabsrmmButtonPressed = NULL; hServiceContactMenu = CreateServiceFunction(MS_PASTEIT_CONTACTMENU, ContactMenuService); contactWindows = new std::map(); return 0; } extern "C" int __declspec(dllexport) Unload(void) { UnhookEvent(hModulesLoaded); UnhookEvent(hPrebuildContactMenu); UnhookEvent(hOptionsInit); if(hWindowEvent != NULL) UnhookEvent(hWindowEvent); DestroyServiceFunction(hServiceContactMenu); Netlib_CloseHandle(g_hNetlibUser); if(hTabsrmmButtonPressed != NULL) UnhookEvent(hTabsrmmButtonPressed); for(int i=0; i < PasteToWeb::pages; ++i) if(pasteToWebs[i] != NULL) { delete pasteToWebs[i]; pasteToWebs[i] = NULL; } if(Options::instance != NULL) { delete Options::instance; Options::instance = NULL; } delete contactWindows; return 0; }