summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <george.hazan@gmail.com>2023-12-24 15:36:52 +0300
committerGeorge Hazan <george.hazan@gmail.com>2023-12-24 15:36:52 +0300
commit93af1070aaa1de81d573ff0ec879e74df036dd01 (patch)
tree502d4f5b3fa538e52acf1d595b36566df312433f
parentc69b6df18206f22513bc83f93d29f95ecbd77977 (diff)
ICQ: added messages' forwarding + "Add to favorites" NewStory menu item
-rw-r--r--protocols/ICQ-WIM/res/Forward.icobin0 -> 4286 bytes
-rw-r--r--protocols/ICQ-WIM/res/resources.rc11
-rw-r--r--protocols/ICQ-WIM/src/main.cpp1
-rw-r--r--protocols/ICQ-WIM/src/menus.cpp172
-rw-r--r--protocols/ICQ-WIM/src/proto.cpp27
-rw-r--r--protocols/ICQ-WIM/src/proto.h13
-rw-r--r--protocols/ICQ-WIM/src/resource.h4
-rw-r--r--protocols/ICQ-WIM/src/server.cpp41
8 files changed, 203 insertions, 66 deletions
diff --git a/protocols/ICQ-WIM/res/Forward.ico b/protocols/ICQ-WIM/res/Forward.ico
new file mode 100644
index 0000000000..0b6fa81f6c
--- /dev/null
+++ b/protocols/ICQ-WIM/res/Forward.ico
Binary files 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 <http://www.gnu.org/licenses/>.
#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<CIcqProto>
{
+ friend class CForwardDlg;
friend struct AsyncRapiRequest;
class CIcqProtoImpl
@@ -207,6 +212,7 @@ class CIcqProto : public PROTO<CIcqProto>
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<CIcqProto>
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<CIcqProto>
////////////////////////////////////////////////////////////////////////////////////////
// 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);