From dd61627f93d5f40f530fa71b827716afa3c7c79e Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 19 May 2012 17:13:01 +0000 Subject: added NewAwaySysMod (not compiled yet) git-svn-id: http://svn.miranda-ng.org/main/trunk@81 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/NewAwaySysMod/AwaySys.cpp | 1123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1123 insertions(+) create mode 100644 plugins/NewAwaySysMod/AwaySys.cpp (limited to 'plugins/NewAwaySysMod/AwaySys.cpp') diff --git a/plugins/NewAwaySysMod/AwaySys.cpp b/plugins/NewAwaySysMod/AwaySys.cpp new file mode 100644 index 0000000000..c335ee1dac --- /dev/null +++ b/plugins/NewAwaySysMod/AwaySys.cpp @@ -0,0 +1,1123 @@ +/* + New Away System - plugin for Miranda IM + Copyright (c) 2005-2007 Chervov Dmitry + Copyright (c) 2004-2005 Iksaif Entertainment + Copyright (c) 2002-2003 Goblineye Entertainment + + 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 +*/ + +/* + Thanx to Faith Healer for icons and help, Unregistered for his plugins (variables, AAA :p), + mistag for GamerStatus, BigMuscle for his code to use AAA + Thanx to Tornado, orignal developer of AwaySys. + Please note that some code from the Miranda's original away module (SRAway) is used around + AwaySys. I tried to mention it wherever possible, but I might have forgotten a few. Kudos to Miranda's authors. + The Read-Away-Msg part was practically copied from Miranda, not proud of it, but since I really can't see how can I make it better, there + was no point in rewriting it all. +*/ + +#define _DECL_DLLMAIN +#include // needed for MSVC 7 msvcr7*.dll patch +#include "Common.h" +#include "m_genmenu.h" +#include "m_idle.h" +#include "m_statusplugins.h" +#include "m_updater.h" +#include "..\CommonLibs\m_NewAwaySys.h" +#include "..\CommonLibs\m_ContactSettings.h" +#include "MsgTree.h" +#include "ContactList.h" +#include "Properties.h" +#include "Path.h" +#include "Services.h" +#include "VersionNo.h" + +//NightFox +#include + +HINSTANCE g_hInstance; +PLUGINLINK *pluginLink; +TMyArray hHooks, hServices; +HANDLE g_hContactMenuItem = NULL, g_hReadStatMenuItem = NULL, /*g_hTopToolbarbutton = NULL, */g_hToggleSOEMenuItem = NULL, g_hToggleSOEContactMenuItem = NULL, g_hAutoreplyOnContactMenuItem = NULL, g_hAutoreplyOffContactMenuItem = NULL, g_hAutoreplyUseDefaultContactMenuItem = NULL; +bool g_fNoProcessing = false; // tells the status change proc not to do anything +int g_bIsIdle = false; +HANDLE hMainThread; +int g_CSProtoCount = 0; // CommonStatus - StartupStatus and AdvancedAutoAway +VAR_PARSE_DATA VarParseData; +int (*g_OldCallService)(const char *, WPARAM, LPARAM) = NULL; + +static struct +{ + int Status, DisableReplyCtlID, DontShowDialogCtlID; +} StatusModeList[] = { + ID_STATUS_ONLINE, IDC_REPLYDLG_DISABLE_ONL, IDC_MOREOPTDLG_DONTPOPDLG_ONL, + ID_STATUS_AWAY, IDC_REPLYDLG_DISABLE_AWAY, IDC_MOREOPTDLG_DONTPOPDLG_AWAY, + ID_STATUS_NA, IDC_REPLYDLG_DISABLE_NA, IDC_MOREOPTDLG_DONTPOPDLG_NA, + ID_STATUS_OCCUPIED, IDC_REPLYDLG_DISABLE_OCC, IDC_MOREOPTDLG_DONTPOPDLG_OCC, + ID_STATUS_DND, IDC_REPLYDLG_DISABLE_DND, IDC_MOREOPTDLG_DONTPOPDLG_DND, + ID_STATUS_FREECHAT, IDC_REPLYDLG_DISABLE_FFC, IDC_MOREOPTDLG_DONTPOPDLG_FFC, + ID_STATUS_INVISIBLE, IDC_REPLYDLG_DISABLE_INV, IDC_MOREOPTDLG_DONTPOPDLG_INV, + ID_STATUS_ONTHEPHONE, IDC_REPLYDLG_DISABLE_OTP, IDC_MOREOPTDLG_DONTPOPDLG_OTP, + ID_STATUS_OUTTOLUNCH, IDC_REPLYDLG_DISABLE_OTL, IDC_MOREOPTDLG_DONTPOPDLG_OTL +}; + +// took this nice idea from MetaContacts plugin ;) +// my_make_version is required to break up #define PRODUCTVER from VersionNo.h +DWORD my_make_version(const int a, const int b, const int c, const int d) +{ + return PLUGIN_MAKE_VERSION(a, b, c, d); +} + +PLUGININFOEX pluginInfo = { + sizeof(PLUGININFOEX), + "New Away System Mod (" +#ifdef _DEBUG + "DEBUG " +#endif +#ifdef _UNICODE + "Unicode" +#else + "ANSI" +#endif + ")", + 0, // see VersionNo.h + "New Away System Mod plugin for Miranda IM. Build #"STRSPECIALBUILD" [ "__DATE__" "__TIME__ +#ifdef _DEBUG + " DEBUG" +#endif +#ifdef _UNICODE + " Unicode" +#else + " ANSI" +#endif + " ]", + "NightFox; Deathdemon; XF007; Goblineye Entertainment", + "NightFox@myied.org", + "© 2010 NightFox; © 2005-2007 Chervov Dmitry; © 2004-2005 Iksaif; © 2002-2003 Goblineye Entertainment", + "http://MyiEd.org/packs", + UNICODE_AWARE, + DEFMOD_SRAWAY, // mwawawawa. +#ifdef _UNICODE +// {0x75cc4fef, 0xb038, 0x4224, {0xb3, 0x75, 0x7, 0x21, 0xf9, 0x76, 0x18, 0x13}} +// {75CC4FEF-B038-4224-B375-0721F9761813} + {0xb2dd9270, 0xce5e, 0x11df, {0xbd, 0x3d, 0x8, 0x0, 0x20, 0xc, 0x9a, 0x66}} +// {b2dd9270-ce5d-11df-bd3b-0800200c9a66} +#else +// {0x313e808f, 0x7162, 0x4319, {0xae, 0xe, 0xcc, 0xad, 0x4d, 0xe5, 0xeb, 0x71}} +// {313E808F-7162-4319-AE0E-CCAD4DE5EB71} + {0x14254a00, 0xce5e, 0x11df, {0xbd, 0x3d, 0x8, 0x0, 0x20, 0xc, 0x9a, 0x66}} +// {14254a00-ce5e-11df-bd3b-0800200c9a66} + +#endif +}; + +PLUGININFO oldPluginInfo = { + sizeof(PLUGININFO), + pluginInfo.shortName, + pluginInfo.version, + pluginInfo.description, + pluginInfo.author, + pluginInfo.authorEmail, + pluginInfo.copyright, + pluginInfo.homepage, + pluginInfo.flags, + pluginInfo.replacesDefaultModule +}; + +BOOL WINAPI MyDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + g_hInstance = hinstDLL; +// have to call _CRT_INIT here, and redefine DLL entry point to MyDllMain because of +// msvcr71.dll's __CppXcptFilter function dependency, which is not present in msvcrt.dll + return _CRT_INIT(hinstDLL, fdwReason, lpvReserved); +} + +static const MUUID interfaces[] = {MIID_SRAWAY, MIID_LAST}; // TODO: add MIID_WHOISREADING here if there'll be any some time in future.. +extern "C" __declspec(dllexport) const MUUID *MirandaPluginInterfaces(void) +{ + return interfaces; +} + +extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) +{ + pluginInfo.version = my_make_version(PRODUCTVER); + return &pluginInfo; +} + +extern "C" __declspec(dllexport) PLUGININFO *MirandaPluginInfo(DWORD mirandaVersion) +{ + oldPluginInfo.version = my_make_version(PRODUCTVER); + return &oldPluginInfo; +} + +//NightFox +int ModernOptInitialise(WPARAM wParam,LPARAM lParam); + +TCString GetDynamicStatMsg(HANDLE hContact, char *szProto, DWORD UIN, int iStatus) +{ +// hContact is the contact that requests the status message + if (hContact != INVALID_HANDLE_VALUE) + { + VarParseData.Message = CContactSettings(iStatus, hContact).GetMsgFormat(GMF_ANYCURRENT, NULL, szProto); + } else + { // contact is unknown + VarParseData.Message = CProtoSettings(szProto, iStatus).GetMsgFormat(iStatus ? GMF_LASTORDEFAULT : GMF_ANYCURRENT); + } + TCString sTime; + VarParseData.szProto = szProto ? szProto : ((hContact && hContact != INVALID_HANDLE_VALUE) ? (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0) : NULL); + VarParseData.UIN = UIN; + VarParseData.Flags = 0; + if (ServiceExists(MS_VARS_FORMATSTRING) && !g_SetAwayMsgPage.GetDBValueCopy(IDS_SAWAYMSG_DISABLEVARIABLES)) + { + FORMATINFO fi = {0}; + fi.cbSize = sizeof(fi); + fi.tszFormat = VarParseData.Message; + fi.hContact = hContact; + fi.flags = FIF_TCHAR; + TCHAR *szResult = (TCHAR*)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0); + if (szResult) + { + VarParseData.Message = szResult; + CallService(MS_VARS_FREEMEMORY, (WPARAM)szResult, 0); + } + } + return VarParseData.Message = VarParseData.Message.Left(AWAY_MSGDATA_MAX); +} + + +int StatusMsgReq(WPARAM wParam, LPARAM lParam, CString &szProto) +{ + _ASSERT(szProto != NULL); + LogMessage("ME_ICQ_STATUSMSGREQ called. szProto=%s, Status=%d, UIN=%d", (char*)szProto, wParam, lParam); +// find the contact + char *szFoundProto; + HANDLE hFoundContact = NULL; // if we'll find the contact only on some other protocol, but not on szProto, then we'll use that hContact. + HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + char *szCurProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (DBGetContactSettingDword(hContact, szCurProto, "UIN", 0) == lParam) + { + szFoundProto = szCurProto; + hFoundContact = hContact; + if (!strcmp(szCurProto, szProto)) + { + break; + } + } + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + int iMode = ICQStatusToGeneralStatus(wParam); + if (!hFoundContact) + { + hFoundContact = INVALID_HANDLE_VALUE; + } else if (iMode >= ID_STATUS_ONLINE && iMode <= ID_STATUS_OUTTOLUNCH) + { // don't count xstatus requests + DBWriteContactSettingWord(hFoundContact, MOD_NAME, DB_REQUESTCOUNT, DBGetContactSettingWord(hFoundContact, MOD_NAME, DB_REQUESTCOUNT, 0) + 1); + } + HANDLE hContactForSettings = hFoundContact; // used to take into account not-on-list contacts when getting contact settings, but at the same time allows to get correct contact info for contacts that are in the DB + if (hContactForSettings != INVALID_HANDLE_VALUE && DBGetContactSettingByte(hContactForSettings, "CList", "NotOnList", 0)) + { + hContactForSettings = INVALID_HANDLE_VALUE; // INVALID_HANDLE_VALUE means the contact is not-on-list + } + if (g_SetAwayMsgPage.GetWnd()) + { + CallAllowedPS_SETAWAYMSG(szProto, iMode, NULL); // we can set status messages to NULL here, as they'll be changed again when the SAM dialog closes. + return 0; + } + if (CContactSettings(iMode, hContactForSettings).Ignore) + { + CallAllowedPS_SETAWAYMSG(szProto, iMode, ""); // currently NULL makes ICQ to ignore _any_ further status message requests until the next PS_SETAWAYMSG, so I can't use it here.. + return 0; // move along, sir + } + + if (iMode) + { // if it's not an xstatus message request + CallAllowedPS_SETAWAYMSG(szProto, iMode, (char*)TCHAR2ANSI(GetDynamicStatMsg(hFoundContact, szProto, lParam))); + } +// COptPage PopupNotifyData(g_PopupOptPage); +// PopupNotifyData.DBToMem(); + VarParseData.szProto = szProto; + VarParseData.UIN = lParam; + VarParseData.Flags = 0; + if (!iMode) + { + VarParseData.Flags |= VPF_XSTATUS; + } +/* + int ShowPopup = ((iMode == ID_STATUS_ONLINE && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_ONLNOTIFY)) || + (iMode == ID_STATUS_AWAY && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_AWAYNOTIFY)) || + (iMode == ID_STATUS_DND && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_DNDNOTIFY)) || + (iMode == ID_STATUS_NA && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_NANOTIFY)) || + (iMode == ID_STATUS_OCCUPIED && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_OCCNOTIFY)) || + (iMode == ID_STATUS_FREECHAT && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_FFCNOTIFY)) || + (!iMode && PopupNotifyData.GetValue(IDC_POPUPOPTDLG_OTHERNOTIFY))) && + CContactSettings(iMode, hContactForSettings).PopupNotify.IncludingParents(); + +// Show popup and play a sound + if (ShowPopup) + { + ShowPopup = ShowPopupNotification(PopupNotifyData, hFoundContact, iMode); // we need ShowPopup also to determine whether to log to file or not + } +*/ +// Log status message request to a file +// if (!PopupNotifyData.GetValue(IDC_POPUPOPTDLG_LOGONLYWITHPOPUP) || ShowPopup) +// { + TCString LogMsg; + if (!iMode) + { // if it's an xstatus message request + LogMsg = DBGetContactSettingString(NULL, szProto, "XStatusName", _T("")); + TCString XMsg(DBGetContactSettingString(NULL, szProto, "XStatusMsg", _T(""))); + if (XMsg.GetLen()) + { + if (LogMsg.GetLen()) + { + LogMsg += _T("\r\n"); + } + LogMsg += XMsg; + } + } else + { + LogMsg = VarParseData.Message; + } + if (ServiceExists(MS_VARS_FORMATSTRING)) + { + logservice_log(LOG_ID, hFoundContact, LogMsg); + } else + { + TCString szUIN; + _ultot(lParam, szUIN.GetBuffer(16), 10); + szUIN.ReleaseBuffer(); + TCHAR *szStatDesc = iMode ? (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, iMode, 0) : STR_XSTATUSDESC; + if (!szStatDesc) + { + _ASSERT(0); + szStatDesc = _T(""); + } + logservice_log(LOG_ID, hFoundContact, TCString((TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hFoundContact, GCDNF_TCHAR)) + _T(" (") + szUIN + TranslateT(") read your ") + szStatDesc + TranslateT(" message:\r\n") + LogMsg); + } +// } + return 0; +} + +// Here is an ugly workaround to support multiple ICQ accounts +// hope 5 icq accounts will be sufficient for everyone ;) +#define MAXICQACCOUNTS 5 +CString ICQProtoList[MAXICQACCOUNTS]; +#define StatusMsgReqN(N) int StatusMsgReq##N(WPARAM wParam, LPARAM lParam) {return StatusMsgReq(wParam, lParam, ICQProtoList[N - 1]);} +StatusMsgReqN(1) StatusMsgReqN(2) StatusMsgReqN(3) StatusMsgReqN(4) StatusMsgReqN(5) +MIRANDAHOOK StatusMsgReqHooks[] = {StatusMsgReq1, StatusMsgReq2, StatusMsgReq3, StatusMsgReq4, StatusMsgReq5}; + +int IsAnICQProto(char *szProto) +{ + int I; + for (I = 0; I < MAXICQACCOUNTS; I++) + { + if (ICQProtoList[I] == (const char*)szProto) + { + return true; + } + } + return false; +} + + +int StatusChanged(WPARAM wParam, LPARAM lParam) +{ +// wParam = iMode +// lParam = (char*)szProto + LogMessage("MS_CLIST_SETSTATUSMODE called. szProto=%s, Status=%d", lParam ? (char*)lParam : "NULL", wParam); + g_ProtoStates[(char*)lParam].Status = wParam; +// let's check if we handle this thingy + if (g_fNoProcessing) // we're told not to do anything + { + g_fNoProcessing = false; // take it off + return 0; + } + DWORD Flag1 = 0; + DWORD Flag3 = 0; + if (lParam) + { + Flag1 = CallProtoService((char*)lParam, PS_GETCAPS, PFLAGNUM_1, 0); + Flag3 = CallProtoService((char*)lParam, PS_GETCAPS, PFLAGNUM_3, 0); + } else + { + PROTOCOLDESCRIPTOR **proto; + int iProtoCount = 0; + CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&iProtoCount, (LPARAM)&proto); + int I; + for (I = 0; I < iProtoCount; I++) + { + if (proto[I]->type == PROTOTYPE_PROTOCOL) + { + Flag1 |= CallProtoService(proto[I]->szName, PS_GETCAPS, PFLAGNUM_1, 0); + Flag3 |= CallProtoService(proto[I]->szName, PS_GETCAPS, PFLAGNUM_3, 0); + } + } + } + if (!(Flag1 & PF1_MODEMSGSEND || Flag3 & Proto_Status2Flag(wParam) || (Flag1 & PF1_IM) == PF1_IM)) + { + return 0; // there are no protocols with changed status that support autoreply or away messages for this status + } + if (g_SetAwayMsgPage.GetWnd()) + { + SetForegroundWindow(g_SetAwayMsgPage.GetWnd()); + return 0; + } + int I; + for (I = lengthof(StatusModeList) - 1; I >= 0; I--) + { + if (wParam == StatusModeList[I].Status) + { + break; + } + } + if (I < 0) + { + return 0; + } + BOOL bScreenSaverRunning; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &bScreenSaverRunning, 0); + if (bScreenSaverRunning || g_MoreOptPage.GetDBValueCopy(StatusModeList[I].DontShowDialogCtlID)) + { + CProtoSettings((char*)lParam).SetMsgFormat(SMF_PERSONAL, CProtoSettings((char*)lParam).GetMsgFormat(GMF_LASTORDEFAULT)); + ChangeProtoMessages((char*)lParam, wParam, TCString()); + } else + { + SetAwayMsgData *dat = new SetAwayMsgData; + ZeroMemory(dat, sizeof(SetAwayMsgData)); + dat->szProtocol = (char*)lParam; + dat->IsModeless = false; + DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_SETAWAYMSG), NULL, SetAwayMsgDlgProc, (LPARAM)dat); + } + return 0; +} + + +#define ID_STATUS_LAST 40081 // yes, 40081 means internal CommonStatus' ID_STATUS_LAST here, not ID_STATUS_IDLE :-S +#define ID_STATUS_CURRENT 40082 +#define ID_STATUS_DISABLED 41083 +int CSStatusChange(WPARAM wParam, LPARAM lParam) // CommonStatus plugins (StartupStatus and AdvancedAutoAway) +{ +// wParam = PROTOCOLSETTINGEX** protoSettings + PROTOCOLSETTINGEX** ps = *(PROTOCOLSETTINGEX***)wParam; + if (!ps) + { + return -1; + } + LogMessage("ME_CS_STATUSCHANGEEX event:"); + int I; + for (I = 0; I < g_CSProtoCount; I++) + { + LogMessage("%d: cbSize=%d, szProto=%s, status=%d, lastStatus=%d, szMsg:", I + 1, ps[I]->cbSize, ps[I]->szName ? (char*)ps[I]->szName : "NULL", ps[I]->status, ps[I]->lastStatus, ps[I]->szMsg ? ps[I]->szMsg : "NULL"); + if (ps[I]->status != ID_STATUS_DISABLED) + { + if (ps[I]->status != ID_STATUS_CURRENT) + { + g_ProtoStates[ps[I]->szName].Status = (ps[I]->status == ID_STATUS_LAST) ? ps[I]->lastStatus : ps[I]->status; + } + CProtoSettings(ps[I]->szName).SetMsgFormat(SMF_TEMPORARY, ps[I]->szMsg ? ANSI2TCHAR(ps[I]->szMsg) : CProtoSettings(ps[I]->szName).GetMsgFormat(GMF_LASTORDEFAULT)); + } + } + return 0; +} + + +static int IdleChangeEvent(WPARAM wParam, LPARAM lParam) +{ + LogMessage("ME_IDLE_CHANGED event. lParam=0x%x", lParam); // yes, we don't do anything with status message changes on idle.. there seems to be no any good solution for the wrong status message issue :( + g_bIsIdle = lParam & IDF_ISIDLE; + return 0; +} + + +int CSModuleLoaded(WPARAM wParam, LPARAM lParam) // StartupStatus and AdvancedAutoAway +{ +// wParam = ProtoCount + g_CSProtoCount = wParam; + return 0; +} + + +int PreBuildContactMenu(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + CLISTMENUITEM miSetMsg = {0}; + miSetMsg.cbSize = sizeof(miSetMsg); + miSetMsg.flags = CMIM_FLAGS | CMIF_TCHAR | CMIF_HIDDEN; + CLISTMENUITEM miReadMsg = {0}; + miReadMsg.cbSize = sizeof(miReadMsg); + miReadMsg.flags = CMIM_FLAGS | CMIF_TCHAR | CMIF_HIDDEN; + int iMode = szProto ? CallProtoService(szProto, PS_GETSTATUS, 0, 0) : 0; + int Flag1 = szProto ? CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) : 0; + int iContactMode = DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE); + TCHAR szSetStr[256], szReadStr[256]; + if (szProto) + { + int I; + for (I = lengthof(StatusModeList) - 1; I >= 0; I--) + { + if (iMode == StatusModeList[I].Status) + { + break; + } + } + if ((Flag1 & PF1_MODEMSGSEND && CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(iMode)) || ((Flag1 & PF1_IM) == PF1_IM && (I < 0 || !g_AutoreplyOptPage.GetDBValueCopy(StatusModeList[I].DisableReplyCtlID)))) + { // the protocol supports status message sending for current status, or autoreplying + _stprintf(szSetStr, TranslateT("Set %s message for the contact"), CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, iMode, GCMDF_TCHAR), CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR)); + miSetMsg.ptszName = szSetStr; + miSetMsg.flags = CMIM_FLAGS | CMIF_TCHAR | CMIM_NAME; + } + if (Flag1 & PF1_MODEMSGRECV && CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(iContactMode)) + { // the protocol supports status message reading for contact's status + _stprintf(szReadStr, TranslateT("Re&ad %s Message"), CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, iContactMode, GCMDF_TCHAR)); + miReadMsg.ptszName = szReadStr; + miReadMsg.flags = CMIM_FLAGS | CMIF_TCHAR | CMIM_NAME | CMIM_ICON; + miReadMsg.hIcon = LoadSkinnedProtoIcon(szProto, iContactMode); + } + } + if (g_hContactMenuItem) + { + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hContactMenuItem, (LPARAM)&miSetMsg); + + if ((Flag1 & PF1_IM) == PF1_IM) + { // if this contact supports sending/receiving messages + int iAutoreply = CContactSettings(g_ProtoStates[szProto].Status, hContact).Autoreply; + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_ICON | CMIM_FLAGS | CMIF_TCHAR; + switch (iAutoreply) + { + case VAL_USEDEFAULT: mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DOT)); break; + case 0: mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SOE_DISABLED)); break; + default: iAutoreply = 1; mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SOE_ENABLED)); break; + } + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hToggleSOEContactMenuItem, (LPARAM)&mi); + mi.flags = CMIM_FLAGS | CMIF_TCHAR | (iAutoreply == 1 ? CMIF_CHECKED : 0); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hAutoreplyOnContactMenuItem, (LPARAM)&mi); + mi.flags = CMIM_FLAGS | CMIF_TCHAR | (iAutoreply == 0 ? CMIF_CHECKED : 0); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hAutoreplyOffContactMenuItem, (LPARAM)&mi); + mi.flags = CMIM_FLAGS | CMIF_TCHAR | (iAutoreply == VAL_USEDEFAULT ? CMIF_CHECKED : 0); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hAutoreplyUseDefaultContactMenuItem, (LPARAM)&mi); + } else + { // hide the Autoreply menu item + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_FLAGS | CMIF_TCHAR | CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hToggleSOEContactMenuItem, (LPARAM)&mi); + } + } + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hReadStatMenuItem, (LPARAM)&miReadMsg); + return 0; +} + + +static int SetContactStatMsg(WPARAM wParam, LPARAM lParam) +{ + if (g_SetAwayMsgPage.GetWnd()) // already setting something + { + SetForegroundWindow(g_SetAwayMsgPage.GetWnd()); + return 0; + } + SetAwayMsgData *dat = new SetAwayMsgData; + ZeroMemory(dat, sizeof(SetAwayMsgData)); + dat->hInitContact = (HANDLE)wParam; + dat->szProtocol = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0); + dat->IsModeless = false; + DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_SETAWAYMSG), NULL, SetAwayMsgDlgProc, (LPARAM)dat); + return 0; +} + +/* //NightFox: deleted used-to-be support +void UpdateSOEButtons(HANDLE hContact) +{ + if (!hContact) + { + int SendOnEvent = CContactSettings(g_ProtoStates[(char*)NULL].Status).Autoreply; + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)g_hTopToolbarbutton, SendOnEvent ? TTBST_PUSHED : TTBST_RELEASED); + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.position = 1000020000; + mi.flags = CMIF_TCHAR | CMIM_NAME | CMIM_ICON; // strange, but CMIF_TCHAR is still necessary even without CMIM_FLAGS + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(SendOnEvent ? IDI_SOE_ENABLED : IDI_SOE_DISABLED)); + mi.ptszName = SendOnEvent ? DISABLE_SOE_COMMAND : ENABLE_SOE_COMMAND; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hToggleSOEMenuItem, (LPARAM)&mi); + } + if (g_SetAwayMsgPage.GetWnd()) + { + SendMessage(g_SetAwayMsgPage.GetWnd(), UM_SAM_REPLYSETTINGCHANGED, (WPARAM)hContact, 0); + } +} +*/ + +int ToggleSendOnEvent(WPARAM wParam, LPARAM lParam) +{ // used only for the global setting + HANDLE hContact = (HANDLE)wParam; + CContactSettings(g_ProtoStates[hContact ? (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0) : (char*)NULL].Status, hContact).Autoreply.Toggle(); + //UpdateSOEButtons(); + return 0; +} + + +int srvAutoreplyOn(WPARAM wParam, LPARAM lParam) +{ // wParam = hContact + HANDLE hContact = (HANDLE)wParam; + CContactSettings(g_ProtoStates[(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0)].Status, hContact).Autoreply = 1; + //UpdateSOEButtons(hContact); + return 0; +} + + +int srvAutoreplyOff(WPARAM wParam, LPARAM lParam) +{ // wParam = hContact + HANDLE hContact = (HANDLE)wParam; + CContactSettings(g_ProtoStates[(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0)].Status, hContact).Autoreply = 0; + //UpdateSOEButtons(hContact); + return 0; +} + + +int srvAutoreplyUseDefault(WPARAM wParam, LPARAM lParam) +{ // wParam = hContact + HANDLE hContact = (HANDLE)wParam; + CContactSettings(g_ProtoStates[(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0)].Status, hContact).Autoreply = VAL_USEDEFAULT; + //UpdateSOEButtons(hContact); + return 0; +} + +/* //NightFox: deleted used-to-be support +int Create_TopToolbar(WPARAM wParam, LPARAM lParam) +{ + int SendOnEvent = CContactSettings(g_ProtoStates[(char*)NULL].Status).Autoreply; + if (ServiceExists(MS_TTB_ADDBUTTON)) + { + TTBButton ttbb = {0}; + ttbb.cbSize = sizeof(ttbb); + ttbb.hbBitmapUp = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_SOE_DISABLED)); + ttbb.hbBitmapDown = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_SOE_ENABLED)); + ttbb.pszServiceUp = MS_AWAYSYS_AUTOREPLY_TOGGLE; + ttbb.pszServiceDown = MS_AWAYSYS_AUTOREPLY_TOGGLE; + ttbb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP; + ttbb.name = Translate("Toggle autoreply on/off"); + g_hTopToolbarbutton = (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)&ttbb, 0); + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)g_hTopToolbarbutton, SendOnEvent ? TTBST_PUSHED : TTBST_RELEASED); + } + return 0; +} +*/ + +static int IconsChanged(WPARAM wParam, LPARAM lParam) +{ + g_IconList.ReloadIcons(); + if (g_MessagesOptPage.GetWnd()) + { + SendMessage(g_MessagesOptPage.GetWnd(), UM_ICONSCHANGED, 0, 0); + } + if (g_MoreOptPage.GetWnd()) + { + SendMessage(g_MoreOptPage.GetWnd(), UM_ICONSCHANGED, 0, 0); + } + if (g_AutoreplyOptPage.GetWnd()) + { + SendMessage(g_AutoreplyOptPage.GetWnd(), UM_ICONSCHANGED, 0, 0); + } +/* if (g_PopupOptPage.GetWnd()) + { + SendMessage(g_PopupOptPage.GetWnd(), UM_ICONSCHANGED, 0, 0); + }*/ + return 0; +} + + +static int ContactSettingsInit(WPARAM wParam, LPARAM lParam) +{ + CONTACTSETTINGSINIT *csi = (CONTACTSETTINGSINIT*)wParam; + char *szProto = (csi->Type == CSIT_CONTACT) ? (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)csi->hContact, 0) : NULL; + if ((csi->Type == CSIT_GROUP) || szProto) + { + int Flag1 = (csi->Type == CSIT_CONTACT) ? CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) : PF1_IM; // we assume that there can be some contacts in the group with PF1_IM capability + if ((Flag1 & PF1_IM) == PF1_IM || Flag1 & PF1_INDIVMODEMSG) + { // does contact's protocol supports message sending/receiving or individual status messages? + CONTACTSETTINGSCONTROL csc = {0}; + csc.cbSize = sizeof(csc); + csc.cbStateSize = sizeof(CSCONTROLSTATE); + csc.Position = CSPOS_SORTBYALPHABET; + csc.ControlType = CSCT_CHECKBOX; + csc.szModule = MOD_NAME; + csc.StateNum = 3; + csc.DefState = 2; // these settings are used for all controls below + + /*if ((csi->Type == CSIT_GROUP) || IsAnICQProto(szProto)) + { + csc.Flags = CSCF_TCHAR; + csc.ptszTitle = LPGENT("New Away System: Status message request notifications"); + csc.ptszGroup = CSGROUP_NOTIFICATIONS; + csc.ptszTooltip = NULL; + csc.szSetting = DB_POPUPNOTIFY; + CallService(MS_CONTACTSETTINGS_ADDCONTROL, wParam, (LPARAM)&csc); + }*/ + int StatusMode = 0; + if (csi->Type == CSIT_CONTACT) + { + CContactSettings CSettings(0, csi->hContact); + StatusMode = CSettings.Status; + } else + { + _ASSERT(csi->Type == CSIT_GROUP); + StatusMode = g_ProtoStates[(char*)NULL].Status; + } + if (StatusMode == ID_STATUS_OFFLINE) + { + StatusMode = ID_STATUS_AWAY; + } + CString Setting; + TCHAR Title[128]; + csc.Flags = CSCF_TCHAR | CSCF_DONT_TRANSLATE_STRINGS; // these Flags and ptszGroup are used for both controls below + csc.ptszGroup = TranslateT("New Away System"); + + if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_PERSTATUSPERSONALSETTINGS)) + { + _stprintf(Title, TranslateT("Enable autoreply when you are %s"), (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, StatusMode, GCMDF_TCHAR)); + csc.ptszTitle = Title; + csc.ptszTooltip = TranslateT("\"Store contact autoreply/ignore settings for each status separately\" is enabled, so this setting is per-contact AND per-status."); + } else + { + csc.ptszTitle = TranslateT("Enable autoreply"); + csc.ptszTooltip = NULL; + } + Setting = StatusToDBSetting(StatusMode, DB_ENABLEREPLY, IDC_MOREOPTDLG_PERSTATUSPERSONALSETTINGS); + csc.szSetting = Setting; + CallService(MS_CONTACTSETTINGS_ADDCONTROL, wParam, (LPARAM)&csc); + + if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_PERSTATUSPERSONALSETTINGS)) + { + _stprintf(Title, TranslateT("Don't send status message when you are %s"), (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, StatusMode, GCMDF_TCHAR)); + csc.ptszTitle = Title; + csc.ptszTooltip = TranslateT("Ignore status message requests from this contact and don't send an autoreply.\r\n\"Store contact autoreply/ignore settings for each status separately\" is enabled, so this setting is per-contact AND per-status."); + } else + { + csc.ptszTitle = TranslateT("Don't send status message"); + csc.ptszTooltip = TranslateT("Ignore status message requests from this contact and don't send an autoreply"); + } + Setting = StatusToDBSetting(StatusMode, DB_IGNOREREQUESTS, IDC_MOREOPTDLG_PERSTATUSPERSONALSETTINGS); + csc.szSetting = Setting; + CallService(MS_CONTACTSETTINGS_ADDCONTROL, wParam, (LPARAM)&csc); + } + } + return 0; +} + + +int srvVariablesHandler(WPARAM wParam, LPARAM lParam) +{ + ARGUMENTSINFO *ai = (ARGUMENTSINFO*)lParam; + ai->flags = AIF_DONTPARSE; + TCString Result; + if (!lstrcmp(ai->targv[0], _T(VAR_AWAYSINCE_TIME))) + { + GetTimeFormat(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, g_ProtoStates[VarParseData.szProto].AwaySince, (ai->argc > 1 && *ai->targv[1]) ? ai->targv[1] : _T("H:mm"), Result.GetBuffer(256), 256); + Result.ReleaseBuffer(); + } else if (!lstrcmp(ai->targv[0], _T(VAR_AWAYSINCE_DATE))) + { + GetDateFormat(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, g_ProtoStates[VarParseData.szProto].AwaySince, (ai->argc > 1 && *ai->targv[1]) ? ai->targv[1] : NULL, Result.GetBuffer(256), 256); + Result.ReleaseBuffer(); + } else if (!lstrcmp(ai->targv[0], _T(VAR_STATDESC))) + { + Result = (VarParseData.Flags & VPF_XSTATUS) ? STR_XSTATUSDESC : (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, g_ProtoStates[VarParseData.szProto].Status, GCMDF_TCHAR); + } else if (!lstrcmp(ai->targv[0], _T(VAR_MYNICK))) + { + if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_MYNICKPERPROTO) && VarParseData.szProto) + { + Result = DBGetContactSettingString(NULL, VarParseData.szProto, "Nick", (TCHAR*)NULL); + } + if (Result == NULL) + { + Result = (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, NULL, GCDNF_TCHAR); + } + if (Result == NULL) + { + Result = TranslateT("Stranger"); + } + } else if (!lstrcmp(ai->targv[0], _T(VAR_REQUESTCOUNT))) + { + _stprintf(Result.GetBuffer(16), _T("%d"), DBGetContactSettingWord(ai->fi->hContact, MOD_NAME, DB_REQUESTCOUNT, 0)); + Result.ReleaseBuffer(); + } else if (!lstrcmp(ai->targv[0], _T(VAR_MESSAGENUM))) + { + _stprintf(Result.GetBuffer(16), _T("%d"), DBGetContactSettingWord(ai->fi->hContact, MOD_NAME, DB_MESSAGECOUNT, 0)); + Result.ReleaseBuffer(); + } else if (!lstrcmp(ai->targv[0], _T(VAR_TIMEPASSED))) + { + ULARGE_INTEGER ul_AwaySince, ul_Now; + SYSTEMTIME st; + GetLocalTime(&st); + SystemTimeToFileTime(&st, (LPFILETIME)&ul_Now); + SystemTimeToFileTime(g_ProtoStates[VarParseData.szProto].AwaySince, (LPFILETIME)&ul_AwaySince); + ul_Now.QuadPart -= ul_AwaySince.QuadPart; + ul_Now.QuadPart /= 10000000; // now it's in seconds + Result.GetBuffer(256); + if (ul_Now.LowPart >= 7200) // more than 2 hours + { + _stprintf(Result, TranslateT("%d hours"), ul_Now.LowPart / 3600); + } else if (ul_Now.LowPart >= 120) // more than 2 minutes + { + _stprintf(Result, TranslateT("%d minutes"), ul_Now.LowPart / 60); + } else + { + _stprintf(Result, TranslateT("%d seconds"), ul_Now.LowPart); + } + Result.ReleaseBuffer(); + } else if (!lstrcmp(ai->targv[0], _T(VAR_PREDEFINEDMESSAGE))) + { + ai->flags = 0; // reset AIF_DONTPARSE flag + if (ai->argc != 2) + { + return NULL; + } + COptPage MsgTreeData(g_MsgTreePage); + COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE); + TreeCtrl->DBToMem(CString(MOD_NAME)); + int I; + for (I = 0; I < TreeCtrl->Value.GetSize(); I++) + { + if (!(TreeCtrl->Value[I].Flags & TIF_GROUP) && !_tcsicmp(TreeCtrl->Value[I].Title, ai->targv[1])) + { + Result = TreeCtrl->Value[I].User_Str1; + break; + } + } + if (Result == NULL) + { // if we didn't find a message with specified title + return NULL; // return it now, as later we change NULL to "" + } + } else if (!lstrcmp(ai->targv[0], _T(VAR_PROTOCOL))) + { + if (VarParseData.szProto) + { + CString AnsiResult; + CallProtoService(VarParseData.szProto, PS_GETNAME, 256, (LPARAM)AnsiResult.GetBuffer(256)); + AnsiResult.ReleaseBuffer(); + Result = ANSI2TCHAR(AnsiResult); + } + if (Result == NULL) + { // if we didn't find a message with specified title + return NULL; // return it now, as later we change NULL to "" + } + } + TCHAR *szResult; + if (!(szResult = (TCHAR*)malloc((Result.GetLen() + 1) * sizeof(TCHAR)))) + { + return NULL; + } + _tcscpy(szResult, (Result != NULL) ? Result : _T("")); + return (int)szResult; +} + + +int srvFreeVarMem(WPARAM wParam, LPARAM lParam) +{ + if (!lParam) + { + return -1; + } + free((void*)lParam); + return 0; +} + + +static int MyCallService(const char *name, WPARAM wParam, LPARAM lParam) +{ + if (name && wParam <= ID_STATUS_OUTTOLUNCH && wParam >= ID_STATUS_OFFLINE) // wParam conditions here are distinctive "features" of PS_SETSTATUS and PS_SETAWAYMSG services, so if wParam does not suit them, we'll pass the control to the old CallService function as soon as possible + { + char *pProtoNameEnd = strrchr(name, '/'); + if (pProtoNameEnd) + { + if (!lstrcmpA(pProtoNameEnd, PS_SETSTATUS)) + { + // it's PS_SETSTATUS service; wParam = status; lParam = 0 + // returns 0 on success, nonzero on failure + CString Proto(""); + Proto.DiffCat(name, pProtoNameEnd); + if (wParam != g_ProtoStates[Proto].Status) + { + g_ProtoStates[Proto].Status = wParam; + TCString Msg(CProtoSettings(Proto).GetMsgFormat(GMF_LASTORDEFAULT)); + LogMessage("Detected a PS_SETSTATUS call with Status different from the one known to NAS. szProto=%s, NewStatus=%d, NewMsg:\n%s", (char*)Proto, wParam, (Msg != NULL) ? TCHAR2ANSI(Msg) : "NULL"); + CProtoSettings(Proto).SetMsgFormat(SMF_TEMPORARY, Msg); + } + } else if (!lstrcmpA(pProtoNameEnd, PS_SETAWAYMSG)) + { + // PS_SETAWAYMSG service; wParam = status; lParam = (const char*)szMessage + // returns 0 on success, nonzero on failure + CString Proto(""); + Proto.DiffCat(name, pProtoNameEnd); + LogMessage("Someone else than NAS called PS_SETAWAYMSG. szProto=%s, Status=%d, Msg:\n%s", (char*)Proto, wParam, lParam ? (char*)lParam : "NULL"); + CProtoSettings(Proto).SetMsgFormat(SMF_TEMPORARY, lParam ? ((ServiceExists(MS_VARS_FORMATSTRING) && !g_SetAwayMsgPage.GetDBValueCopy(IDS_SAWAYMSG_DISABLEVARIABLES)) ? VariablesEscape(ANSI2TCHAR((char*)lParam)) : ANSI2TCHAR((char*)lParam)) : TCString(_T(""))); + ChangeProtoMessages(Proto, wParam, TCString()); + return 0; + } + } + } + return g_OldCallService(name, wParam, lParam); +} + + +int MirandaLoaded(WPARAM wParam, LPARAM lParam) +{ + LoadMsgTreeModule(); + LoadCListModule(); + InitUpdateMsgs(); + g_IconList.ReloadIcons(); + + PROTOCOLDESCRIPTOR **proto; + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hMainThread, THREAD_SET_CONTEXT, false, 0); + int iProtoCount = 0; + CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&iProtoCount, (LPARAM)&proto); + int I; + int CurProtoIndex; + for (I = 0, CurProtoIndex = 0; I < iProtoCount && CurProtoIndex < MAXICQACCOUNTS; I++) + { + if (proto[I]->type == PROTOTYPE_PROTOCOL) + { + HANDLE hHook = HookEvent(CString(proto[I]->szName) + ME_ICQ_STATUSMSGREQ, StatusMsgReqHooks[CurProtoIndex]); + if (hHook) + { + hHooks.AddElem(hHook); + ICQProtoList[CurProtoIndex] = proto[I]->szName; + CurProtoIndex++; + } + } + } + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_SETCONTACTSTATMSG, SetContactStatMsg)); + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_AUTOREPLY_TOGGLE, ToggleSendOnEvent)); + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_AUTOREPLY_ON, srvAutoreplyOn)); + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_AUTOREPLY_OFF, srvAutoreplyOff)); + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_AUTOREPLY_USEDEFAULT, srvAutoreplyUseDefault)); + hServices.AddElem(CreateServiceFunction(MS_NAS_GETSTATEA, GetStateA)); + hServices.AddElem(CreateServiceFunction(MS_NAS_SETSTATEA, SetStateA)); + hServices.AddElem(CreateServiceFunction(MS_NAS_GETSTATEW, GetStateW)); + hServices.AddElem(CreateServiceFunction(MS_NAS_SETSTATEW, SetStateW)); + hServices.AddElem(CreateServiceFunction(MS_NAS_INVOKESTATUSWINDOW, InvokeStatusWindow)); + hServices.AddElem(CreateServiceFunction(MS_AWAYMSG_GETSTATUSMSG, GetStatusMsg)); +// and old AwaySysMod service, for compatibility reasons + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_SETSTATUSMODE, SetStatusMode)); +//NightFox: none; +// hHooks.AddElem(HookEvent(ME_TTB_MODULELOADED, Create_TopToolbar)); + hHooks.AddElem(HookEvent(ME_OPT_INITIALISE, OptsDlgInit)); + hHooks.AddElem(HookEvent(ME_CLIST_STATUSMODECHANGE, StatusChanged)); + hHooks.AddElem(HookEvent(ME_CS_STATUSCHANGEEX, CSStatusChange)); // for compatibility with StartupStatus and AdvancedAutoAway + hHooks.AddElem(HookEvent(ME_DB_EVENT_FILTER_ADD, MsgEventAdded)); + hHooks.AddElem(HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu)); + hHooks.AddElem(HookEvent(ME_SKIN_ICONSCHANGED, IconsChanged)); + hHooks.AddElem(HookEvent(ME_IDLE_CHANGED, IdleChangeEvent)); + hHooks.AddElem(HookEvent(ME_CONTACTSETTINGS_INITIALISE, ContactSettingsInit)); + g_hReadWndList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0); + int SendOnEvent = CContactSettings(g_ProtoStates[(char*)NULL].Status).Autoreply; + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.position = 1000020000; + mi.flags = CMIF_TCHAR | CMIF_NOTOFFLINE; + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(SendOnEvent ? IDI_SOE_ENABLED : IDI_SOE_DISABLED)); + mi.ptszName = SendOnEvent ? DISABLE_SOE_COMMAND : ENABLE_SOE_COMMAND; + mi.pszService = MS_AWAYSYS_AUTOREPLY_TOGGLE; + g_hToggleSOEMenuItem = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&mi); + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + mi.position = -2000005000; + mi.flags = CMIF_TCHAR | CMIF_NOTOFFLINE | CMIF_HIDDEN; + mi.hIcon = NULL; + mi.pszContactOwner = NULL; + mi.ptszName = LPGENT("Read status message"); // never seen... + mi.pszService = MS_AWAYMSG_SHOWAWAYMSG; + g_hReadStatMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_USEMENUITEM)) + { + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + mi.flags = CMIF_TCHAR | CMIF_HIDDEN; + mi.ptszName = LPGENT("Set status message"); // will never be shown + mi.position = 1000020000; + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MSGICON)); + mi.pszService = MS_AWAYSYS_SETCONTACTSTATMSG; + g_hContactMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + mi.flags = CMIF_TCHAR | CMIF_ROOTPOPUP; + mi.hIcon = NULL; + mi.pszPopupName = (char*)-1; + mi.position = 1000020000; + mi.ptszName = LPGENT("Autoreply"); + g_hToggleSOEContactMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + mi.flags = CMIF_TCHAR | CMIF_CHILDPOPUP; + mi.pszPopupName = (char*)g_hToggleSOEContactMenuItem; + mi.popupPosition = 1000020000; + mi.position = 1000020000; + + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SOE_ENABLED)); + mi.ptszName = LPGENT("On"); + mi.pszService = MS_AWAYSYS_AUTOREPLY_ON; + g_hAutoreplyOnContactMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SOE_DISABLED)); + mi.ptszName = LPGENT("Off"); + mi.pszService = MS_AWAYSYS_AUTOREPLY_OFF; + g_hAutoreplyOffContactMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + mi.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DOT)); + mi.ptszName = LPGENT("Use the default setting"); + mi.pszService = MS_AWAYSYS_AUTOREPLY_USEDEFAULT; + g_hAutoreplyUseDefaultContactMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + } + // add that funky thingy (just tweaked a bit, was spotted in Miranda's src code) + // we have to read the status message from contacts too... err + hServices.AddElem(CreateServiceFunction(MS_AWAYMSG_SHOWAWAYMSG, GetContactStatMsg)); + + SkinAddNewSound(AWAYSYS_STATUSMSGREQUEST_SOUND, Translate("NewAwaySys: Incoming status message request"), ""); + if (ServiceExists(MS_VARS_REGISTERTOKEN)) + { + struct + { + TCHAR *Name; + char *Descr; + int Flags; + } Variables[] = { + _T(VAR_AWAYSINCE_TIME), LPGEN("New Away System\t(x)\tAway since time in default format; ?nas_awaysince_time(x) in format x"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_AWAYSINCE_DATE), LPGEN("New Away System\t(x)\tAway since date in default format; ?nas_awaysince_date(x) in format x"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_STATDESC), LPGEN("New Away System\tStatus description"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_MYNICK), LPGEN("New Away System\tYour nick for current protocol"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_REQUESTCOUNT), LPGEN("New Away System\tNumber of status message requests from the contact"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_MESSAGENUM), LPGEN("New Away System\tNumber of messages from the contact"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_TIMEPASSED), LPGEN("New Away System\tTime passed until request"), TRF_FIELD | TRF_FUNCTION, + _T(VAR_PREDEFINEDMESSAGE), LPGEN("New Away System\t(x)\tReturns one of your predefined messages by its title: ?nas_predefinedmessage(creepy)"), TRF_FUNCTION, + _T(VAR_PROTOCOL), LPGEN("New Away System\tCurrent protocol name"), TRF_FIELD | TRF_FUNCTION + }; + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_FREEVARMEM, srvFreeVarMem)); + hServices.AddElem(CreateServiceFunction(MS_AWAYSYS_VARIABLESHANDLER, srvVariablesHandler)); + TOKENREGISTER tr = {0}; + tr.cbSize = sizeof(TOKENREGISTER); + tr.szService = MS_AWAYSYS_VARIABLESHANDLER; + tr.szCleanupService = MS_AWAYSYS_FREEVARMEM; + tr.memType = TR_MEM_OWNER; + int I; + for (I = 0; I < lengthof(Variables); I++) + { + tr.flags = Variables[I].Flags | TRF_CALLSVC | TRF_TCHAR; + tr.tszTokenString = Variables[I].Name; + tr.szHelpText = Variables[I].Descr; + CallService(MS_VARS_REGISTERTOKEN, 0, (LPARAM)&tr); + } + } +// updater plugin support + Update update = {0}; + char szVersion[16]; + update.cbSize = sizeof(Update); + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE*)CreateVersionString(my_make_version(PRODUCTVER), szVersion); + update.cpbVersion = strlen((char*)update.pbVersion); + update.szUpdateURL = "http://myied.org/packs/NAS" +#ifdef _UNICODE + "W" +#endif + ".zip"; + update.szVersionURL = "http://myied.org/packs/NAS/updaterinfo.php"; + update.pbVersionPrefix = (BYTE*)"New Away System Mod" +#ifdef _UNICODE + " Unicode" +#endif + " version "; + update.cpbVersionPrefix = strlen((char*)update.pbVersionPrefix); + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + + //NightFox + HookEvent(ME_MODERNOPT_INITIALIZE, ModernOptInitialise); + + + return 0; +} + + +extern "C" int __declspec(dllexport) Load(PLUGINLINK *link) +{ + pluginLink = link; + if (CallService(MS_SYSTEM_GETVERSION, 0, 0) < 0x060000) + { + MessageBox(NULL, TranslateT("New Away System plugin requires Miranda version 0.6.0.0 or above."), TranslateT("New Away System"), MB_OK); + return 1; + } + hHooks.AddElem(HookEvent(ME_SYSTEM_MODULESLOADED, MirandaLoaded)); +// hHooks.AddElem(HookEvent(ME_CS_CSMODULELOADED, CSModuleLoaded)); // compatibility with StartupStatus and AdvancedAutoAway + if (DBGetContactSettingString(NULL, "KnownModules", "New Away System", (char*)NULL) == NULL) + { + DBWriteContactSettingString(NULL, "KnownModules", "New Away System", MOD_NAME); + } + + InitCommonControls(); + InitOptions(); // must be called before we hook CallService + g_OldCallService = pluginLink->CallService; // looks like this hack is currently the only way to handle all calls to PS_SETSTATUS and PS_SETAWAYMSG properly. I must know when someone calls it, and there's no any "standard" way to know the real value passed as wParam to it (protocol module often replaces it by another, _supported_ status; for example, Jabber replaces Occupied by DND), so we're intercepting the service here +// and we need to know the real wParam value just to set proper status message (an example: changing the global status to Out to lunch makes Jabber go Away. I guess, the user wants Jabber status message to be "I've been having lunch..." too, like for other protocols, rather than the default Away message "Gone since...") + pluginLink->CallService = MyCallService; + + logservice_register(LOG_ID, LPGENT("New Away System"), _T("NewAwaySys?puts(p,?dbsetting(%subject%,Protocol,p))?if2(_?dbsetting(,?get(p),?pinfo(?get(p),uidsetting)),).log"), TranslateTS(_T("`[`!cdate()-!ctime()`]` ?cinfo(%subject%,display) (?cinfo(%subject%,id)) read your %") _T(VAR_STATDESC) _T("% message:\r\n%extratext%\r\n\r\n"))); + + if (DBGetContactSettingByte(NULL, MOD_NAME, DB_SETTINGSVER, 0) < 1) + { // change all %nas_message% variables to %extratext% if it wasn't done before + TCString Str; + Str = DBGetContactSettingString(NULL, MOD_NAME, "PopupsFormat", _T("")); + if (Str.GetLen()) + { + DBWriteContactSettingTString(NULL, MOD_NAME, "PopupsFormat", Str.Replace(_T("nas_message"), _T("extratext"))); + } + Str = DBGetContactSettingString(NULL, MOD_NAME, "ReplyPrefix", _T("")); + if (Str.GetLen()) + { + DBWriteContactSettingTString(NULL, MOD_NAME, "ReplyPrefix", Str.Replace(_T("nas_message"), _T("extratext"))); + } + } + if (DBGetContactSettingByte(NULL, MOD_NAME, DB_SETTINGSVER, 0) < 2) + { // disable autoreply for not-on-list contacts, as such contact may be a spam bot + DBWriteContactSettingByte(NULL, MOD_NAME, ContactStatusToDBSetting(0, DB_ENABLEREPLY, 0, INVALID_HANDLE_VALUE), 0); + DBWriteContactSettingByte(NULL, MOD_NAME, DB_SETTINGSVER, 2); + } + return 0; +} + + +extern "C" int __declspec(dllexport) Unload() +{ + _ASSERT(pluginLink->CallService == MyCallService); + if (pluginLink->CallService == MyCallService) + { + pluginLink->CallService = g_OldCallService; + } + CloseHandle(hMainThread); + int I; + for (I = 0; I < hHooks.GetSize(); I++) + { + if (hHooks[I]) + { + UnhookEvent(hHooks[I]); + } + } + for (I = 0; I < hServices.GetSize(); I++) + { + if (hServices[I]) + { + DestroyServiceFunction(hServices[I]); + } + } + return 0; +} -- cgit v1.2.3