void ChangeProtoMessages(char* szProto, int iMode, TCString &Msg) { TCString CurMsg(Msg); if (szProto) { if (Msg == NULL) { CurMsg = GetDynamicStatMsg(INVALID_HANDLE_VALUE, szProto); } CallAllowedPS_SETAWAYMSG(szProto, iMode, (char*)TCHAR2ANSI(CurMsg)); g_ProtoStates[szProto].CurStatusMsg = CurMsg; } else // change message of all protocols { int ProtoCount; PROTOCOLDESCRIPTOR **proto; CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&proto); int I; for (I = 0; I < ProtoCount; I++) { if (proto[I]->type == PROTOTYPE_PROTOCOL && !DBGetContactSettingByte(NULL, proto[I]->szName, "LockMainStatus", 0)) { if (Msg == NULL) { CurMsg = GetDynamicStatMsg(INVALID_HANDLE_VALUE, proto[I]->szName); } CallAllowedPS_SETAWAYMSG(proto[I]->szName, iMode, (char*)TCHAR2ANSI(CurMsg)); g_ProtoStates[proto[I]->szName].CurStatusMsg = CurMsg; } } } static struct { int Status; char *Setting; } StatusSettings[] = { ID_STATUS_OFFLINE, "Off", ID_STATUS_ONLINE, "On", ID_STATUS_AWAY, "Away", ID_STATUS_NA, "Na", ID_STATUS_DND, "Dnd", ID_STATUS_OCCUPIED, "Occupied", ID_STATUS_FREECHAT, "FreeChat", ID_STATUS_INVISIBLE, "Inv", ID_STATUS_ONTHEPHONE, "Otp", ID_STATUS_OUTTOLUNCH, "Otl", ID_STATUS_IDLE, "Idl" }; int I; for (I = 0; I < lengthof(StatusSettings); I++) { if (iMode == StatusSettings[I].Status) { DBWriteContactSettingTString(NULL, "SRAway", CString(StatusSettings[I].Setting) + "Msg", CurMsg); DBWriteContactSettingTString(NULL, "SRAway", CString(StatusSettings[I].Setting) + "Default", CurMsg); // TODO: make it more accurate, and change not only here, but when changing status messages through UpdateMsgsTimerFunc too; and when changing messages through AutoAway() ? break; } } // InitUpdateMsgs(); }
void ChangeProtoMessages(char* szProto, int iMode, TCString &Msg) { TCString CurMsg(Msg); if (szProto) { if (Msg == NULL) CurMsg = GetDynamicStatMsg(INVALID_CONTACT_ID, szProto); CallAllowedPS_SETAWAYMSG(szProto, iMode, (char*)_T2A(CurMsg)); g_ProtoStates[szProto].CurStatusMsg = CurMsg; } else { // change message of all protocols int numAccs; PROTOACCOUNT **accs; ProtoEnumAccounts(&numAccs, &accs); for (int i = 0; i < numAccs; i++) { PROTOACCOUNT *p = accs[i]; if (!db_get_b(NULL, p->szModuleName, "LockMainStatus", 0)) { if (Msg == NULL) CurMsg = GetDynamicStatMsg(INVALID_CONTACT_ID, p->szModuleName); CallAllowedPS_SETAWAYMSG(p->szModuleName, iMode, (char*)_T2A(CurMsg)); g_ProtoStates[p->szModuleName].CurStatusMsg = CurMsg; } } } static struct { int Status; char *Setting; } StatusSettings[] = { ID_STATUS_OFFLINE, "Off", ID_STATUS_ONLINE, "On", ID_STATUS_AWAY, "Away", ID_STATUS_NA, "Na", ID_STATUS_DND, "Dnd", ID_STATUS_OCCUPIED, "Occupied", ID_STATUS_FREECHAT, "FreeChat", ID_STATUS_INVISIBLE, "Inv", ID_STATUS_ONTHEPHONE, "Otp", ID_STATUS_OUTTOLUNCH, "Otl", ID_STATUS_IDLE, "Idl" }; for (int i = 0; i < SIZEOF(StatusSettings); i++) { if (iMode == StatusSettings[i].Status) { db_set_ts(NULL, "SRAway", CString(StatusSettings[i].Setting) + "Msg", CurMsg); db_set_ts(NULL, "SRAway", CString(StatusSettings[i].Setting) + "Default", CurMsg); // TODO: make it more accurate, and change not only here, but when changing status messages through UpdateMsgsTimerFunc too; and when changing messages through AutoAway() ? break; } } // InitUpdateMsgs(); }
void __cdecl UpdateMsgsThreadProc(void *) { int ProtoCount; PROTOCOLDESCRIPTOR **proto; CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&proto); int I; while (WaitForSingleObject(g_hTerminateUpdateMsgsThread, 0) == WAIT_TIMEOUT && !Miranda_Terminated()) { DWORD MinUpdateTimeDifference = g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_UPDATEMSGSPERIOD) * 1000; // in milliseconds for (I = 0; I < ProtoCount; I++) { if (proto[I]->type == PROTOTYPE_PROTOCOL && CallProtoService(proto[I]->szName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND && !IsAnICQProto(proto[I]->szName)) { int Status = CallProtoService(proto[I]->szName, PS_GETSTATUS, 0, 0); if (Status < ID_STATUS_OFFLINE || Status > ID_STATUS_OUTTOLUNCH) { Status = g_ProtoStates[proto[I]->szName].Status; } if (CallProtoService(proto[I]->szName, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(Status) && g_ProtoStates[proto[I]->szName].CurStatusMsg.GetUpdateTimeDifference() >= MinUpdateTimeDifference) { TCString CurMsg(GetDynamicStatMsg(INVALID_HANDLE_VALUE, proto[I]->szName)); if ((TCString)g_ProtoStates[proto[I]->szName].CurStatusMsg != (const TCHAR*)CurMsg) // if the message has changed { g_ProtoStates[proto[I]->szName].CurStatusMsg = CurMsg; CallAllowedPS_SETAWAYMSG(proto[I]->szName, Status, (char*)TCHAR2ANSI(CurMsg)); } } } } SleepEx(PARSE_INTERVAL, true); } }
void __cdecl UpdateMsgsThreadProc(void *) { int numAccs; PROTOACCOUNT **accs; ProtoEnumAccounts(&numAccs, &accs); while (WaitForSingleObject(g_hTerminateUpdateMsgsThread, 0) == WAIT_TIMEOUT && !Miranda_Terminated()) { DWORD MinUpdateTimeDifference = g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_UPDATEMSGSPERIOD) * 1000; // in milliseconds for (int i = 0; i < numAccs; i++) { PROTOACCOUNT *p = accs[i]; if (CallProtoService(p->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND && !IsAnICQProto(p->szModuleName)) { int Status = CallProtoService(p->szModuleName, PS_GETSTATUS, 0, 0); if (Status < ID_STATUS_OFFLINE || Status > ID_STATUS_OUTTOLUNCH) { Status = g_ProtoStates[p->szModuleName].Status; } if (CallProtoService(p->szModuleName, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(Status) && g_ProtoStates[p->szModuleName].CurStatusMsg.GetUpdateTimeDifference() >= MinUpdateTimeDifference) { TCString CurMsg(GetDynamicStatMsg(INVALID_CONTACT_ID, p->szModuleName)); if ((TCString)g_ProtoStates[p->szModuleName].CurStatusMsg != (const TCHAR*)CurMsg) { // if the message has changed g_ProtoStates[p->szModuleName].CurStatusMsg = CurMsg; CallAllowedPS_SETAWAYMSG(p->szModuleName, Status, (char*)_T2A(CurMsg)); } } } } SleepEx(PARSE_INTERVAL, true); } }
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; MCONTACT hFoundContact = NULL; // if we'll find the contact only on some other protocol, but not on szProto, then we'll use that hContact. for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { char *szCurProto = GetContactProto(hContact); if (db_get_dw(hContact, szCurProto, "UIN", 0) == lParam) { szFoundProto = szCurProto; hFoundContact = hContact; if (!mir_strcmp(szCurProto, szProto)) break; } } int iMode = ICQStatusToGeneralStatus(wParam); if (!hFoundContact) hFoundContact = INVALID_CONTACT_ID; else if (iMode >= ID_STATUS_ONLINE && iMode <= ID_STATUS_OUTTOLUNCH) // don't count xstatus requests db_set_w(hFoundContact, MOD_NAME, DB_REQUESTCOUNT, db_get_w(hFoundContact, MOD_NAME, DB_REQUESTCOUNT, 0) + 1); MCONTACT 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_CONTACT_ID && db_get_b(hContactForSettings, "CList", "NotOnList", 0)) hContactForSettings = INVALID_CONTACT_ID; // 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, _T("")); // 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, 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; return 0; }
int MsgEventAdded(WPARAM wParam, LPARAM lParam) { HANDLE hContact = (HANDLE)wParam; DBEVENTINFO *dbei = (DBEVENTINFO*)lParam; if (!hContact) { return 0; } if (dbei->flags & DBEF_SENT || (dbei->eventType != EVENTTYPE_MESSAGE && dbei->eventType != EVENTTYPE_URL && dbei->eventType != EVENTTYPE_FILE)) { return 0; } if (time(NULL) - dbei->timestamp > MAX_REPLY_TIMEDIFF) { // don't reply to offline messages return 0; } char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (!szProto) { return 0; } DWORD Flags1 = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0); if (!(Flags1 & PF1_IMSEND)) { // don't reply to protocols that don't support outgoing messages return 0; } int bMsgWindowIsOpen = MSGWNDOPEN_UNDEFINED; if (dbei->flags & DBEF_READ) { HANDLE hMetaContact; if (ServiceExists(MS_MC_GETMETACONTACT) && (hMetaContact = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0))) // if it's a subcontact of a metacontact { // ugly workaround for metacontacts, part II // remove outdated events first DWORD CurTime = time(NULL); int I; for (I = MetacontactEvents.GetSize() - 1; I >= 0; I--) { if (CurTime - MetacontactEvents[I].timestamp > MAX_REPLY_TIMEDIFF) { MetacontactEvents.RemoveElem(I); } } // we compare only event timestamps, and do not look at the message itself. it's unlikely that there'll be two events from a contact at the same second, so it's a trade-off between speed and reliability for (I = MetacontactEvents.GetSize() - 1; I >= 0; I--) { if (MetacontactEvents[I].timestamp == dbei->timestamp && MetacontactEvents[I].hMetaContact == hMetaContact) { bMsgWindowIsOpen = MetacontactEvents[I].bMsgWindowIsOpen; break; } } if (I < 0) { _ASSERT(0); return 0; } } else { return 0; } } if (ServiceExists(MS_MC_GETPROTOCOLNAME) && !lstrcmpA(szProto, (char*)CallService(MS_MC_GETPROTOCOLNAME, 0, 0))) { // ugly workaround for metacontacts, part I; store all metacontacts' events to a temporary array, so we'll be able to get the 'source' protocol when subcontact event happens later. we need the protocol to get its status and per-status settings properly // remove outdated events first DWORD CurTime = time(NULL); int I; for (I = MetacontactEvents.GetSize() - 1; I >= 0; I--) { if (CurTime - MetacontactEvents[I].timestamp > MAX_REPLY_TIMEDIFF) { MetacontactEvents.RemoveElem(I); } } // add the new event and wait for a subcontact's event MetacontactEvents.AddElem(CMetacontactEvent(hContact, dbei->timestamp, IsSRMsgWindowOpen(hContact, false))); return 0; } unsigned int iMode = CallProtoService(szProto, PS_GETSTATUS, 0, 0); int I; for (I = lengthof(StatusModeList) - 1; I >= 0; I--) { if (iMode == StatusModeList[I].Status) { break; } } if (I < 0) { return 0; } COptPage AutoreplyOptData(g_AutoreplyOptPage); AutoreplyOptData.DBToMem(); if (dbei->eventType == EVENTTYPE_MESSAGE) { DBWriteContactSettingWord(hContact, MOD_NAME, DB_MESSAGECOUNT, DBGetContactSettingWord(hContact, MOD_NAME, DB_MESSAGECOUNT, 0) + 1); // increment message counter } if (AutoreplyOptData.GetValue(StatusModeList[I].DisableReplyCtlID)) { return 0; } HANDLE hContactForSettings = hContact; // 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 (!CContactSettings(iMode, hContactForSettings).Autoreply.IncludingParents(szProto) || CContactSettings(iMode, hContactForSettings).Ignore) { return 0; } if (AutoreplyOptData.GetValue(IDC_REPLYDLG_DONTREPLYINVISIBLE)) { WORD ApparentMode = DBGetContactSettingWord(hContact, szProto, "ApparentMode", 0); if ((iMode == ID_STATUS_INVISIBLE && (!(Flags1 & PF1_INVISLIST) || ApparentMode != ID_STATUS_ONLINE)) || (Flags1 & PF1_VISLIST && ApparentMode == ID_STATUS_OFFLINE)) { return 0; } } if (AutoreplyOptData.GetValue(IDC_REPLYDLG_ONLYCLOSEDDLGREPLY)) { if (bMsgWindowIsOpen && bMsgWindowIsOpen != MSGWNDOPEN_UNDEFINED) { return 0; } // we never get here for a metacontact; we did check for metacontact's window earlier, and here we need to check only for subcontact's window if (IsSRMsgWindowOpen(hContact, false)) { return 0; } } if (AutoreplyOptData.GetValue(IDC_REPLYDLG_ONLYIDLEREPLY) && !g_bIsIdle) { return 0; } int UIN = 0; if (IsAnICQProto(szProto)) { UIN = DBGetContactSettingDword(hContact, szProto, "UIN", 0); } int SendCount = AutoreplyOptData.GetValue(IDC_REPLYDLG_SENDCOUNT); if ((AutoreplyOptData.GetValue(IDC_REPLYDLG_DONTSENDTOICQ) && UIN) || // an icq contact (SendCount != -1 && DBGetContactSettingByte(hContact, MOD_NAME, DB_SENDCOUNT, 0) >= SendCount)) { return 0; } if ((dbei->eventType == EVENTTYPE_MESSAGE && !AutoreplyOptData.GetValue(IDC_REPLYDLG_EVENTMSG)) || (dbei->eventType == EVENTTYPE_URL && !AutoreplyOptData.GetValue(IDC_REPLYDLG_EVENTURL)) || (dbei->eventType == EVENTTYPE_FILE && !AutoreplyOptData.GetValue(IDC_REPLYDLG_EVENTFILE))) { return 0; } DBWriteContactSettingByte(hContact, MOD_NAME, DB_SENDCOUNT, DBGetContactSettingByte(hContact, MOD_NAME, DB_SENDCOUNT, 0) + 1); GetDynamicStatMsg(hContact); // it updates VarParseData.Message needed for %extratext% in the format TCString Reply(*(TCString*)AutoreplyOptData.GetValue(IDC_REPLYDLG_PREFIX)); if (Reply != NULL && ServiceExists(MS_VARS_FORMATSTRING) && !g_SetAwayMsgPage.GetDBValueCopy(IDS_SAWAYMSG_DISABLEVARIABLES)) { FORMATINFO fi = {0}; fi.cbSize = sizeof(FORMATINFO); fi.tszFormat = Reply; fi.hContact = hContact; fi.flags = FIF_TCHAR; fi.tszExtraText = VarParseData.Message; TCHAR *szResult = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0); if (szResult != NULL) { Reply = szResult; mir_free(szResult); } } if (Reply.GetLen()) { CAutoreplyData *ad = new CAutoreplyData(hContact, Reply); mir_forkthread(AutoreplyDelayThread, ad); } return 0; }