From 93af1070aaa1de81d573ff0ec879e74df036dd01 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 24 Dec 2023 15:36:52 +0300 Subject: ICQ: added messages' forwarding + "Add to favorites" NewStory menu item --- protocols/ICQ-WIM/res/Forward.ico | Bin 0 -> 4286 bytes protocols/ICQ-WIM/res/resources.rc | 11 +++ protocols/ICQ-WIM/src/main.cpp | 1 + protocols/ICQ-WIM/src/menus.cpp | 172 +++++++++++++++++++++++++++++-------- protocols/ICQ-WIM/src/proto.cpp | 27 ++---- protocols/ICQ-WIM/src/proto.h | 13 ++- protocols/ICQ-WIM/src/resource.h | 4 +- protocols/ICQ-WIM/src/server.cpp | 41 +++++++-- 8 files changed, 203 insertions(+), 66 deletions(-) create mode 100644 protocols/ICQ-WIM/res/Forward.ico (limited to 'protocols') diff --git a/protocols/ICQ-WIM/res/Forward.ico b/protocols/ICQ-WIM/res/Forward.ico new file mode 100644 index 0000000000..0b6fa81f6c Binary files /dev/null and b/protocols/ICQ-WIM/res/Forward.ico differ diff --git a/protocols/ICQ-WIM/res/resources.rc b/protocols/ICQ-WIM/res/resources.rc index 7af238004a..5f2ec57b50 100644 --- a/protocols/ICQ-WIM/res/resources.rc +++ b/protocols/ICQ-WIM/res/resources.rc @@ -180,6 +180,16 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,102,53,50,14 END +IDD_FORWARD DIALOGEX 0, 0, 215, 267 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Forward message" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x1,8,4,201,239,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "Forward",IDOK,104,248,50,14 + PUSHBUTTON "Cancel",IDCANCEL,158,248,50,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -278,6 +288,7 @@ IDI_INBOX ICON "Mail.ico" IDI_MAIL_NOTIFY ICON "MailNotify.ico" +IDI_FORWARD ICON "Forward.ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/protocols/ICQ-WIM/src/main.cpp b/protocols/ICQ-WIM/src/main.cpp index b445bc7b29..db622413f1 100644 --- a/protocols/ICQ-WIM/src/main.cpp +++ b/protocols/ICQ-WIM/src/main.cpp @@ -84,6 +84,7 @@ static int OnModulesLoaded(WPARAM, LPARAM) static IconItem iconList[] = { + { LPGEN("Forward"), "forward", IDI_FORWARD }, { LPGEN("E-mail"), "icq_email", IDI_INBOX }, { LPGEN("E-mail notification"), "icq_email_notif", IDI_MAIL_NOTIFY } }; diff --git a/protocols/ICQ-WIM/src/menus.cpp b/protocols/ICQ-WIM/src/menus.cpp index b78b308f43..5c7fca94ad 100644 --- a/protocols/ICQ-WIM/src/menus.cpp +++ b/protocols/ICQ-WIM/src/menus.cpp @@ -19,6 +19,22 @@ along with this program. If not, see . #define MenuExecService "/NSExecMenu" +int CIcqProto::OnPrebuildMenu(WPARAM hContact, LPARAM lParam) +{ + if (!Proto_IsProtoOnContact(hContact, m_szModuleName)) { + Menu_ShowItem(hmiForward, false); + Menu_ShowItem(hmiConvert, false); + } + else { + auto *dbei = (DB::EventInfo *)lParam; + Menu_ShowItem(hmiForward, dbei->eventType == EVENTTYPE_MESSAGE || dbei->eventType == EVENTTYPE_FILE); + + ptrW wszText(DbEvent_GetTextW(dbei, CP_UTF8)); + Menu_ShowItem(hmiConvert, fileText2url(wszText.get())); + } + return 0; +} + void CIcqProto::InitMenus() { if (!HookProtoEvent(ME_NS_PREBUILDMENU, &CIcqProto::OnPrebuildMenu)) @@ -31,50 +47,134 @@ void CIcqProto::InitMenus() mi.pszService = szServiceName; mi.position = 1000000; + mi.hIcolibItem = g_plugin.getIconHandle(IDI_FORWARD); + mi.name.a = LPGEN("Forward"); + hmiForward = Menu_AddNewStoryMenuItem(&mi, 1); + + mi.position++; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_ADDCONTACT); + mi.name.a = LPGEN("Add to faforites"); + hmiConvert = Menu_AddNewStoryMenuItem(&mi, 2); + + mi.position++; mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_FILE); mi.name.a = LPGEN("Convert a message into a file transfer"); - hmiConvert = Menu_AddNewStoryMenuItem(&mi, 1); + hmiConvert = Menu_AddNewStoryMenuItem(&mi, 3); } -INT_PTR CIcqProto::SvcExecMenu(WPARAM iCommand, LPARAM pHandle) +///////////////////////////////////////////////////////////////////////////////////////// +// Dialog for message forwarding + +class CForwardDlg : public CIcqDlgBase { - // convert a message into a file transfer - if (iCommand == 1) { - if (MEVENT hEvent = NS_GetCurrent(HANDLE(pHandle))) { - DB::EventInfo dbei(hEvent); - if (!dbei) - return 0; - - IcqFileInfo *pFileInfo = nullptr; - CMStringW wszText(ptrW(DbEvent_GetTextW(&dbei, CP_UTF8))); - if (CheckFile(dbei.hContact, wszText, pFileInfo)) { - if (!pFileInfo || pFileInfo->bIsSticker) { - // sticker is a simple text message prcoessed by SmileyAdd - T2Utf szBody(wszText); - mir_free(dbei.pBlob); - dbei.cbBlob = (int)mir_strlen(szBody.get()); - dbei.pBlob = szBody.detach(); - } - else { - // create the offline file event - dbei.eventType = EVENTTYPE_FILE; - - DB::FILE_BLOB blob(pFileInfo->wszDescr, wszText); - blob.setUrl(pFileInfo->szOrigUrl); - blob.setSize(pFileInfo->dwFileSize); - blob.write(dbei); - } - db_event_edit(hEvent, &dbei); - } - } + CCtrlClc m_clist; + MEVENT m_hEvent; + + void FilterList(CCtrlClc *) + { + for (auto &hContact : Contacts()) + if (!Proto_IsProtoOnContact(hContact, m_proto->m_szModuleName)) + if (HANDLE hItem = m_clist.FindContact(hContact)) + m_clist.DeleteItem(hItem); } - return 0; + + void ResetListOptions(CCtrlClc *) + { + m_clist.SetHideEmptyGroups(true); + m_clist.SetHideOfflineRoot(true); + } + +public: + CForwardDlg(CIcqProto *ppro, MEVENT hEvent) : + CIcqDlgBase(ppro, IDD_FORWARD), + m_hEvent(hEvent), + m_clist(this, IDC_CLIST) + { + m_clist.OnNewContact = + m_clist.OnListRebuilt = Callback(this, &CForwardDlg::FilterList); + m_clist.OnOptionsChanged = Callback(this, &CForwardDlg::ResetListOptions); + } + + bool OnInitDialog() override + { + SetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE, + GetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES); + m_clist.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0); + ResetListOptions(&m_clist); + FilterList(&m_clist); + return true; + } + + bool OnApply() override + { + for (auto &hContact : m_proto->AccContacts()) + if (HANDLE hItem = m_clist.FindContact(hContact)) + if (m_clist.GetCheck(hItem)) + m_proto->ForwardMessage(m_hEvent, hContact); + + return true; + } +}; + +void CIcqProto::ForwardMessage(MEVENT hEvent, MCONTACT to) +{ + DB::EventInfo dbei(hEvent); + if (!dbei || !dbei.szId || mir_strcmp(dbei.szModule, m_szModuleName)) + return; + + CMStringW wszId(GetUserId(dbei.hContact)); + ptrW wszText(DbEvent_GetTextW(&dbei, CP_UTF8)); + + JSONNode parts(JSON_ARRAY); + JSONNode msgText; msgText << CHAR_PARAM("mediaType", "forward") << WCHAR_PARAM("sn", wszId) << INT_PARAM("time", dbei.timestamp) + << CHAR_PARAM("msgId", dbei.szId) << WCHAR_PARAM("text", wszText); + parts.push_back(msgText); + + SendMessageParts(to, parts); } -int CIcqProto::OnPrebuildMenu(WPARAM, LPARAM lParam) +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CIcqProto::SvcExecMenu(WPARAM iCommand, LPARAM pHandle) { - auto *dbei = (DB::EventInfo *)lParam; - ptrW wszText(DbEvent_GetTextW(dbei, CP_UTF8)); - Menu_ShowItem(hmiConvert, fileText2url(wszText.get())); + MEVENT hEvent = NS_GetCurrent(HANDLE(pHandle)); + if (!hEvent) + return 0; + + switch (iCommand) { + case 1: // forward message + CForwardDlg(this, hEvent).DoModal(); + break; + + case 2: // Add to favorites + ForwardMessage(hEvent, m_hFavContact); + + case 3: // convert a message into a file transfer + DB::EventInfo dbei(hEvent); + if (!dbei) + return 0; + + IcqFileInfo *pFileInfo = nullptr; + CMStringW wszText(ptrW(DbEvent_GetTextW(&dbei, CP_UTF8))); + if (CheckFile(dbei.hContact, wszText, pFileInfo)) { + if (!pFileInfo || pFileInfo->bIsSticker) { + // sticker is a simple text message prcoessed by SmileyAdd + T2Utf szBody(wszText); + mir_free(dbei.pBlob); + dbei.cbBlob = (int)mir_strlen(szBody.get()); + dbei.pBlob = szBody.detach(); + } + else { + // create the offline file event + dbei.eventType = EVENTTYPE_FILE; + + DB::FILE_BLOB blob(pFileInfo->wszDescr, wszText); + blob.setUrl(pFileInfo->szOrigUrl); + blob.setSize(pFileInfo->dwFileSize); + blob.write(dbei); + } + db_event_edit(hEvent, &dbei); + } + } return 0; } diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index 9a58298890..b6f27ec26a 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -541,20 +541,6 @@ HANDLE CIcqProto::SendFile(MCONTACT hContact, const wchar_t *szDescription, wcha int CIcqProto::SendMsg(MCONTACT hContact, MEVENT hReplyEvent, const char *pszSrc) { - CMStringA szUserid(GetUserId(hContact)); - if (szUserid.IsEmpty()) - return 0; - - int id = InterlockedIncrement(&m_msgId); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "/im/sendIM", &CIcqProto::OnSendMessage); - - auto *pOwn = new IcqOwnMessage(hContact, id, pReq->m_reqId, pszSrc); - pReq->pUserInfo = pOwn; - { - mir_cslock lck(m_csOwnIds); - m_arOwnIds.insert(pOwn); - } - JSONNode parts(JSON_ARRAY); if (hReplyEvent) { DB::EventInfo dbei(hReplyEvent); @@ -570,10 +556,15 @@ int CIcqProto::SendMsg(MCONTACT hContact, MEVENT hReplyEvent, const char *pszSrc JSONNode msgText; msgText << CHAR_PARAM("mediaType", "text") << CHAR_PARAM("text", pszSrc); parts.push_back(msgText); - - pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "") - << CHAR_PARAM("offlineIM", "true") << CHAR_PARAM("parts", parts.write().c_str()) << CHAR_PARAM("t", szUserid) << INT_PARAM("ts", TS()); - Push(pReq); + + int id = InterlockedIncrement(&m_msgId); + auto *pOwn = new IcqOwnMessage(hContact, id, pszSrc); + { + mir_cslock lck(m_csOwnIds); + m_arOwnIds.insert(pOwn); + } + + SendMessageParts(hContact, parts, pOwn); return id; } diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index 1771a37e43..b236397541 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -146,12 +146,16 @@ struct IcqFileTransfer : public MZeroedObject struct IcqOwnMessage { - IcqOwnMessage(MCONTACT _hContact, int _msgid, const char *guid, const char *pszText) : + IcqOwnMessage(MCONTACT _hContact, int _msgid, const char *pszText) : m_msgid(_msgid), m_hContact(_hContact), m_szText(mir_strdup(pszText)) { - strncpy_s(m_guid, guid, _TRUNCATE); + } + + void setGuid(const char *pszGuid) + { + strncpy_s(m_guid, pszGuid, _TRUNCATE); } MCONTACT m_hContact; @@ -163,6 +167,7 @@ struct IcqOwnMessage class CIcqProto : public PROTO { + friend class CForwardDlg; friend struct AsyncRapiRequest; class CIcqProtoImpl @@ -207,6 +212,7 @@ class CIcqProto : public PROTO void CheckPassword(void); void ConnectionFailed(int iReason, int iErrorCode = 0); void EmailNotification(const wchar_t *pwszText); + void ForwardMessage(MEVENT hEVent, MCONTACT hContact); void GetPermitDeny(); wchar_t* GetUIN(MCONTACT hContact); void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup); @@ -217,6 +223,7 @@ class CIcqProto : public PROTO void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead); void RetrieveUserInfo(MCONTACT hContact); void SendMrimLogin(NETLIBHTTPREQUEST *pReply); + void SendMessageParts(MCONTACT hContact, const JSONNode &parts, IcqOwnMessage *pOwn = nullptr); void SetOwnId(const CMStringW &wszId); void SetServerStatus(int iNewStatus); void ShutdownSession(void); @@ -359,7 +366,7 @@ class CIcqProto : public PROTO //////////////////////////////////////////////////////////////////////////////////////// // Menus - HGENMENU hmiConvert; + HGENMENU hmiForward, hmiConvert; void InitMenus(); diff --git a/protocols/ICQ-WIM/src/resource.h b/protocols/ICQ-WIM/src/resource.h index c0987e795e..38a48a0d35 100644 --- a/protocols/ICQ-WIM/src/resource.h +++ b/protocols/ICQ-WIM/src/resource.h @@ -13,6 +13,8 @@ #define IDD_OPTIONS_ADV 109 #define IDD_LOGINPW 110 #define IDD_EDITGROUPS 111 +#define IDD_FORWARD 112 +#define IDI_FORWARD 113 #define IDC_PASSWORD 1001 #define IDC_UIN 1002 #define IDC_UIN2 1003 @@ -45,7 +47,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_RESOURCE_VALUE 114 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1026 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp index 6d017376a2..3d068a831a 100644 --- a/protocols/ICQ-WIM/src/server.cpp +++ b/protocols/ICQ-WIM/src/server.cpp @@ -1151,7 +1151,8 @@ LBL_Error: int id = InterlockedIncrement(&m_msgId); auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "/im/sendIM", &CIcqProto::OnSendMessage); - auto *pOwn = new IcqOwnMessage(pTransfer->pfts.hContact, id, pReq->m_reqId, T2Utf(wszUrl)); + auto *pOwn = new IcqOwnMessage(pTransfer->pfts.hContact, id, T2Utf(wszUrl)); + pOwn->setGuid(pReq->m_reqId); pOwn->pTransfer = pTransfer; pReq->pUserInfo = pOwn; { @@ -1368,22 +1369,24 @@ void CIcqProto::OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pRe ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)pReq); } +///////////////////////////////////////////////////////////////////////////////////////// +// Send message + void CIcqProto::OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) { IcqOwnMessage *ownMsg = (IcqOwnMessage *)pReq->pUserInfo; JsonReply root(pReply); if (root.error() != 200) { - ProtoBroadcastAck(ownMsg->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)ownMsg->m_msgid); + if (ownMsg) { + ProtoBroadcastAck(ownMsg->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)ownMsg->m_msgid); - mir_cslock lck(m_csOwnIds); - m_arOwnIds.remove(ownMsg); + mir_cslock lck(m_csOwnIds); + m_arOwnIds.remove(ownMsg); + } return; } - if (g_bMessageState) - CallService(MS_MESSAGESTATE_UPDATE, ownMsg->m_hContact, MRD_TYPE_DELIVERED); - const JSONNode &data = root.data(); if (auto &jsonMsg = data.at("histMsgId")) { CMStringA reqId(root.requestId()); @@ -1391,9 +1394,31 @@ void CIcqProto::OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) CheckOwnMessage(reqId, msgId, false); } - CheckLastId(ownMsg->m_hContact, data); + if (ownMsg) { + if (g_bMessageState) + CallService(MS_MESSAGESTATE_UPDATE, ownMsg->m_hContact, MRD_TYPE_DELIVERED); + CheckLastId(ownMsg->m_hContact, data); + } +} + +void CIcqProto::SendMessageParts(MCONTACT hContact, const JSONNode &parts, IcqOwnMessage *pOwn) +{ + CMStringA szUserid(GetUserId(hContact)); + if (szUserid.IsEmpty()) + return; + + auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "/im/sendIM", &CIcqProto::OnSendMessage); + pReq->pUserInfo = pOwn; + if (pOwn) + pOwn->setGuid(pReq->m_reqId); + + pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "") + << CHAR_PARAM("offlineIM", "true") << CHAR_PARAM("parts", parts.write().c_str()) << CHAR_PARAM("t", szUserid) << INT_PARAM("ts", TS()); + Push(pReq); } +///////////////////////////////////////////////////////////////////////////////////////// + void CIcqProto::OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) { JsonReply root(pReply); -- cgit v1.2.3