/*
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"
#include "PasteToWeb1.h"
#include "PasteToWeb2.h"
#include "version.h"
#include "resource.h"
#include "Options.h"
// {1AAC15E8-DCEC-4050-B66F-2AA0E6120C22}
#define MIID_PASTEIT { 0x1aac15e8, 0xdcec, 0x4050, { 0xb6, 0x6f, 0x2a, 0xa0, 0xe6, 0x12, 0xc, 0x22 } }
PLUGINLINK *pluginLink;
PasteToWeb* pasteToWebs[PasteToWeb::pages];
std::map* contactWindows;
DWORD gMirandaVersion;
extern HINSTANCE hInst;
HANDLE hModulesLoaded, hTabsrmmButtonPressed;
HANDLE g_hNetlibUser;
HANDLE hPrebuildContactMenu;
HANDLE hServiceContactMenu;
HGENMENU hContactMenu;
HGENMENU hWebPageMenus[PasteToWeb::pages];
HANDLE hMainIcon;
HANDLE hOptionsInit;
HANDLE hWindowEvent = NULL;
HINSTANCE hInst;
#define MODULE "PasteIt"
#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,
MIID_PASTEIT
};
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;
}
extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PASTEIT, MIID_LAST};
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 = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
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;
CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&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 = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
if (szProto && (INT_PTR)szProto != CALLSERVICE_NOTFOUND) bIsContact = (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IM) ? true : false;
CLISTMENUITEM mi = {0};
mi.cbSize = 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 InitIcolib()
{
TCHAR stzFile[MAX_PATH];
SKINICONDESC sid = {0};
sid.cbSize = sizeof(sid);
sid.cx = sid.cy = 16;
sid.ptszDefaultFile = stzFile;
sid.ptszSection = LPGENT("Paste It");
sid.flags = SIDF_ALL_TCHAR;
GetModuleFileName(hInst, stzFile, MAX_PATH);
sid.pszName = "PasteIt_main";
sid.ptszDescription = LPGENT("Paste It");
sid.iDefaultIndex = -IDI_MENU;
hMainIcon = Skin_AddIcon(&sid);
}
void InitMenuItems()
{
CLISTMENUITEM mi = {0};
CLISTMENUITEM mi2 = {0};
mi.cbSize = sizeof(mi);
mi.flags = CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB | CMIF_TCHAR;
mi.icolibItem = hMainIcon;
mi.position = 3000090005;
mi.ptszName = _T("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 = _T("Paste from clipboard");
Menu_AddContactMenuItem(&mi);
mi.popupPosition = FROM_FILE;
mi.ptszName = _T("Paste from file");
Menu_AddContactMenuItem(&mi);
mi.popupPosition = DEF_PAGES_START - 1;
mi.ptszName = _T("Default web page");
HGENMENU hDefWebMenu = Menu_AddContactMenuItem(&mi);
mi2.cbSize = 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 = {0};
mi.cbSize = 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 = hMainIcon;
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 = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)lParam->hContact, 0);
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)
{
InitIcolib();
InitMenuItems();
InitTabsrmmButton();
hWindowEvent = HookEvent(ME_MSG_WINDOWEVENT, (MIRANDAHOOK)WindowEvent);
return 0;
}
extern "C" int __declspec(dllexport) Load(void)
{
mir_getXI(&xi);
mir_getLP(&pluginInfo);
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;
}