From 1979fd80424d16b2e489f9b57d01d9c7811d25a2 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Mon, 2 Jan 2023 21:10:29 +0300 Subject: Update copyrights --- protocols/JabberG/src/jabber.cpp | 2 +- protocols/JabberG/src/jabber_adhoc.cpp | 2 +- protocols/JabberG/src/jabber_agent.cpp | 2 +- protocols/JabberG/src/jabber_api.cpp | 2 +- protocols/JabberG/src/jabber_archive.cpp | 2 +- protocols/JabberG/src/jabber_bookmarks.cpp | 2 +- protocols/JabberG/src/jabber_byte.cpp | 2 +- protocols/JabberG/src/jabber_byte.h | 2 +- protocols/JabberG/src/jabber_caps.cpp | 2 +- protocols/JabberG/src/jabber_caps.h | 2 +- protocols/JabberG/src/jabber_captcha.cpp | 2 +- protocols/JabberG/src/jabber_chat.cpp | 2 +- protocols/JabberG/src/jabber_console.cpp | 2 +- protocols/JabberG/src/jabber_disco.cpp | 2 +- protocols/JabberG/src/jabber_disco.h | 2 +- protocols/JabberG/src/jabber_events.cpp | 2 +- protocols/JabberG/src/jabber_file.cpp | 2 +- protocols/JabberG/src/jabber_form.cpp | 2 +- protocols/JabberG/src/jabber_ft.cpp | 2 +- protocols/JabberG/src/jabber_groupchat.cpp | 2 +- protocols/JabberG/src/jabber_ibb.cpp | 2 +- protocols/JabberG/src/jabber_ibb.h | 2 +- protocols/JabberG/src/jabber_icolib.cpp | 2 +- protocols/JabberG/src/jabber_icolib.h | 2 +- protocols/JabberG/src/jabber_iq.cpp | 2 +- protocols/JabberG/src/jabber_iq.h | 2 +- protocols/JabberG/src/jabber_iq_handlers.cpp | 2 +- protocols/JabberG/src/jabber_iqid.cpp | 2 +- protocols/JabberG/src/jabber_iqid_muc.cpp | 2 +- protocols/JabberG/src/jabber_libstr.cpp | 2 +- protocols/JabberG/src/jabber_list.cpp | 2 +- protocols/JabberG/src/jabber_list.h | 2 +- protocols/JabberG/src/jabber_mam.cpp | 320 ++-- protocols/JabberG/src/jabber_menu.cpp | 2 +- protocols/JabberG/src/jabber_message_handlers.cpp | 2 +- protocols/JabberG/src/jabber_message_manager.cpp | 2 +- protocols/JabberG/src/jabber_message_manager.h | 2 +- protocols/JabberG/src/jabber_misc.cpp | 2 +- protocols/JabberG/src/jabber_notes.cpp | 2 +- protocols/JabberG/src/jabber_notes.h | 2 +- protocols/JabberG/src/jabber_omemo.cpp | 2 +- protocols/JabberG/src/jabber_omemo.h | 2 +- protocols/JabberG/src/jabber_opt.cpp | 2 +- protocols/JabberG/src/jabber_password.cpp | 2 +- protocols/JabberG/src/jabber_presence_manager.cpp | 2 +- protocols/JabberG/src/jabber_presence_manager.h | 2 +- protocols/JabberG/src/jabber_privacy.cpp | 2 +- protocols/JabberG/src/jabber_privacy.h | 2 +- protocols/JabberG/src/jabber_proto.cpp | 2 +- protocols/JabberG/src/jabber_proto.h | 2 +- protocols/JabberG/src/jabber_rc.cpp | 2 +- protocols/JabberG/src/jabber_rc.h | 2 +- protocols/JabberG/src/jabber_roster.cpp | 1102 ++++++------- protocols/JabberG/src/jabber_search.cpp | 1530 ++++++++--------- protocols/JabberG/src/jabber_search.h | 2 +- protocols/JabberG/src/jabber_secur.cpp | 2 +- protocols/JabberG/src/jabber_secur.h | 2 +- protocols/JabberG/src/jabber_send_manager.cpp | 2 +- protocols/JabberG/src/jabber_send_manager.h | 2 +- protocols/JabberG/src/jabber_strm_mgmt.cpp | 2 +- protocols/JabberG/src/jabber_strm_mgmt.h | 2 +- protocols/JabberG/src/jabber_svc.cpp | 2 +- protocols/JabberG/src/jabber_thread.cpp | 2 +- protocols/JabberG/src/jabber_treelist.cpp | 2 +- protocols/JabberG/src/jabber_userinfo.cpp | 1824 ++++++++++----------- protocols/JabberG/src/jabber_util.cpp | 2 +- protocols/JabberG/src/jabber_vcard.cpp | 2 +- protocols/JabberG/src/jabber_xml.cpp | 2 +- protocols/JabberG/src/jabber_xml.h | 2 +- protocols/JabberG/src/jabber_xstatus.cpp | 2 +- protocols/JabberG/src/jabber_xstatus.h | 2 +- protocols/JabberG/src/jabber_zstream.cpp | 2 +- protocols/JabberG/src/stdafx.cxx | 2 +- protocols/JabberG/src/stdafx.h | 2 +- protocols/JabberG/src/version.h | 2 +- 75 files changed, 2459 insertions(+), 2459 deletions(-) (limited to 'protocols/JabberG') diff --git a/protocols/JabberG/src/jabber.cpp b/protocols/JabberG/src/jabber.cpp index c0fdff4ccd..50dac791a6 100644 --- a/protocols/JabberG/src/jabber.cpp +++ b/protocols/JabberG/src/jabber.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_adhoc.cpp b/protocols/JabberG/src/jabber_adhoc.cpp index d5b6d429f1..1871262e16 100644 --- a/protocols/JabberG/src/jabber_adhoc.cpp +++ b/protocols/JabberG/src/jabber_adhoc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Artem Shpynov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team Module implements an XMPP protocol extension for reporting and executing ad-hoc, human-oriented commands according to XEP-0050: Ad-Hoc Commands diff --git a/protocols/JabberG/src/jabber_agent.cpp b/protocols/JabberG/src/jabber_agent.cpp index a198827672..a8720b5a56 100644 --- a/protocols/JabberG/src/jabber_agent.cpp +++ b/protocols/JabberG/src/jabber_agent.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_api.cpp b/protocols/JabberG/src/jabber_api.cpp index db750517f3..738e1bb439 100644 --- a/protocols/JabberG/src/jabber_api.cpp +++ b/protocols/JabberG/src/jabber_api.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_archive.cpp b/protocols/JabberG/src/jabber_archive.cpp index 97c76993ba..b60f98ac97 100644 --- a/protocols/JabberG/src/jabber_archive.cpp +++ b/protocols/JabberG/src/jabber_archive.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_bookmarks.cpp b/protocols/JabberG/src/jabber_bookmarks.cpp index 0c9944677a..48cc195e17 100644 --- a/protocols/JabberG/src/jabber_bookmarks.cpp +++ b/protocols/JabberG/src/jabber_bookmarks.cpp @@ -3,7 +3,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2007 Michael Stepura, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_byte.cpp b/protocols/JabberG/src/jabber_byte.cpp index 685174347d..060b10ee6c 100644 --- a/protocols/JabberG/src/jabber_byte.cpp +++ b/protocols/JabberG/src/jabber_byte.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_byte.h b/protocols/JabberG/src/jabber_byte.h index 6e3f5b732b..5de0be588c 100644 --- a/protocols/JabberG/src/jabber_byte.h +++ b/protocols/JabberG/src/jabber_byte.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_caps.cpp b/protocols/JabberG/src/jabber_caps.cpp index e49f93d8a8..16ad003a75 100644 --- a/protocols/JabberG/src/jabber_caps.cpp +++ b/protocols/JabberG/src/jabber_caps.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h index fc37993fb0..e8ceccf4a0 100644 --- a/protocols/JabberG/src/jabber_caps.h +++ b/protocols/JabberG/src/jabber_caps.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_captcha.cpp b/protocols/JabberG/src/jabber_captcha.cpp index 641c310395..c2cc8ec2eb 100644 --- a/protocols/JabberG/src/jabber_captcha.cpp +++ b/protocols/JabberG/src/jabber_captcha.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_chat.cpp b/protocols/JabberG/src/jabber_chat.cpp index a32591e576..90bf730ac4 100644 --- a/protocols/JabberG/src/jabber_chat.cpp +++ b/protocols/JabberG/src/jabber_chat.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_console.cpp b/protocols/JabberG/src/jabber_console.cpp index 7f4ab9c19f..cbcfc0f19d 100644 --- a/protocols/JabberG/src/jabber_console.cpp +++ b/protocols/JabberG/src/jabber_console.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2007 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_disco.cpp b/protocols/JabberG/src/jabber_disco.cpp index cca24a8d29..50081898fd 100644 --- a/protocols/JabberG/src/jabber_disco.cpp +++ b/protocols/JabberG/src/jabber_disco.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_disco.h b/protocols/JabberG/src/jabber_disco.h index 48a01bfa93..5f3bd510df 100644 --- a/protocols/JabberG/src/jabber_disco.h +++ b/protocols/JabberG/src/jabber_disco.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2005-07 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_events.cpp b/protocols/JabberG/src/jabber_events.cpp index 043c438c95..da9f81c7bc 100644 --- a/protocols/JabberG/src/jabber_events.cpp +++ b/protocols/JabberG/src/jabber_events.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_file.cpp b/protocols/JabberG/src/jabber_file.cpp index b01b32f32a..f39cbbdf32 100644 --- a/protocols/JabberG/src/jabber_file.cpp +++ b/protocols/JabberG/src/jabber_file.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_form.cpp b/protocols/JabberG/src/jabber_form.cpp index e4e51658ea..e85e91566a 100644 --- a/protocols/JabberG/src/jabber_form.cpp +++ b/protocols/JabberG/src/jabber_form.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_ft.cpp b/protocols/JabberG/src/jabber_ft.cpp index 096351d713..0329d16747 100644 --- a/protocols/JabberG/src/jabber_ft.cpp +++ b/protocols/JabberG/src/jabber_ft.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_groupchat.cpp b/protocols/JabberG/src/jabber_groupchat.cpp index 7fd448657f..f4f3e6c008 100644 --- a/protocols/JabberG/src/jabber_groupchat.cpp +++ b/protocols/JabberG/src/jabber_groupchat.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_ibb.cpp b/protocols/JabberG/src/jabber_ibb.cpp index 22d1f2e41e..67ba5ba2c9 100644 --- a/protocols/JabberG/src/jabber_ibb.cpp +++ b/protocols/JabberG/src/jabber_ibb.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_ibb.h b/protocols/JabberG/src/jabber_ibb.h index b96ae971f9..f620b52ba4 100644 --- a/protocols/JabberG/src/jabber_ibb.h +++ b/protocols/JabberG/src/jabber_ibb.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_icolib.cpp b/protocols/JabberG/src/jabber_icolib.cpp index 4fe3a7d96c..7e08277418 100644 --- a/protocols/JabberG/src/jabber_icolib.cpp +++ b/protocols/JabberG/src/jabber_icolib.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team Idea & portions of code by Artem Shpynov diff --git a/protocols/JabberG/src/jabber_icolib.h b/protocols/JabberG/src/jabber_icolib.h index 7d2c0b1ce2..9066560f1d 100644 --- a/protocols/JabberG/src/jabber_icolib.h +++ b/protocols/JabberG/src/jabber_icolib.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_iq.cpp b/protocols/JabberG/src/jabber_iq.cpp index 9e1dc7dc20..850e755420 100644 --- a/protocols/JabberG/src/jabber_iq.cpp +++ b/protocols/JabberG/src/jabber_iq.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_iq.h b/protocols/JabberG/src/jabber_iq.h index 50694fc73e..01da599737 100644 --- a/protocols/JabberG/src/jabber_iq.h +++ b/protocols/JabberG/src/jabber_iq.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_iq_handlers.cpp b/protocols/JabberG/src/jabber_iq_handlers.cpp index d73785b9f5..bf9f7ad573 100644 --- a/protocols/JabberG/src/jabber_iq_handlers.cpp +++ b/protocols/JabberG/src/jabber_iq_handlers.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp index df222552fa..68783083c7 100644 --- a/protocols/JabberG/src/jabber_iqid.cpp +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_iqid_muc.cpp b/protocols/JabberG/src/jabber_iqid_muc.cpp index 8bf9621ab6..f811d7cb82 100644 --- a/protocols/JabberG/src/jabber_iqid_muc.cpp +++ b/protocols/JabberG/src/jabber_iqid_muc.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_libstr.cpp b/protocols/JabberG/src/jabber_libstr.cpp index 0c20ff4cc2..2b7e91aea5 100644 --- a/protocols/JabberG/src/jabber_libstr.cpp +++ b/protocols/JabberG/src/jabber_libstr.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_list.cpp b/protocols/JabberG/src/jabber_list.cpp index bbfbb1d35e..73abe09e2b 100644 --- a/protocols/JabberG/src/jabber_list.cpp +++ b/protocols/JabberG/src/jabber_list.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_list.h b/protocols/JabberG/src/jabber_list.h index 082d247350..e418131443 100644 --- a/protocols/JabberG/src/jabber_list.h +++ b/protocols/JabberG/src/jabber_list.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_mam.cpp b/protocols/JabberG/src/jabber_mam.cpp index 989630ac07..0665e5df2f 100644 --- a/protocols/JabberG/src/jabber_mam.cpp +++ b/protocols/JabberG/src/jabber_mam.cpp @@ -1,160 +1,160 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team - -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. - -*/ - -#include "stdafx.h" -#include "jabber_iq.h" -#include "jabber_caps.h" - -void CJabberProto::OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) -{ - if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { - if (auto *n = XmlFirstChild(iqNode, "prefs")) { - m_bMamPrefsAvailable = true; - - if (auto *type = n->Attribute("default")) { - if (!strcmp(type, "never")) - m_iMamMode = 0; - else if (!strcmp(type, "roster")) - m_iMamMode = 1; - else - m_iMamMode = 2; - } - } - } - - // shall we retrieve missing messages? - if (pInfo->GetUserData()) - MamRetrieveMissingMessages(); -} - -void CJabberProto::MamSetMode(int iNewMode) -{ - if (!m_bEnableMam) - return; - - const char *szMode; - switch (iNewMode) { - case 0: szMode = "never"; break; - case 1: szMode = "roster"; break; - default: szMode = "always"; break; - } - - XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultMamInfo, JABBER_IQ_TYPE_SET)); - auto *node = iq << XCHILDNS("prefs", JABBER_FEAT_MAM) << XATTR("default", szMode); - node << XCHILD("always"); node << XCHILD("never"); - m_ThreadInfo->send(iq); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CJabberProto::MamRetrieveMissingMessages() -{ - CMStringA szLastId = getMStringA("LastMamId"); - - XmlNodeIq iq("set", SerialNext()); - auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); - - if (szLastId.IsEmpty()) { - m_bMamDisableMessages = true; // our goal is to save message id, not to store messages - m_bMamCreateRead = false; - - char buf[100]; - time2str(time(0), buf, _countof(buf)); - - auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); - form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); - form << XCHILD("field") << XATTR("var", "end") << XCHILD("value", buf); - } - else { - auto *set = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); - set << XCHILD("max", "1000"); - set << XCHILD("after", szLastId); - } - - m_ThreadInfo->send(iq); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Contact's history loader - -void CJabberProto::MamSendForm(const char *pszWith, const char *pszAfter) -{ - auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET); - pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM); - - XmlNodeIq iq(pReq); - auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); - - auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); - form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); - if (pszWith != nullptr) - form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", pszWith); - - auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); - rsm << XCHILD("max", "1000"); - if (pszAfter != nullptr) - rsm << XCHILD("after", pszAfter); - m_ThreadInfo->send(iq); -} - - -void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) -{ - // even if that flag was enabled, unset it - m_bMamDisableMessages = false; - - if (auto *fin = XmlGetChildByTag(iqNode, "fin", "xmlns", JABBER_FEAT_MAM)) { - // if dataset is complete, there's nothing more to do - if (!mir_strcmp(XmlGetAttr(fin, "complete"), "true")) - return; - - if (auto *set = XmlGetChildByTag(fin, "set", "xmlns", "http://jabber.org/protocol/rsm")) - if (auto *lastId = XmlGetChildText(set, "last")) - MamSendForm(ptrA(getUStringA(pInfo->GetHContact(), "jid")), lastId); - } -} - -INT_PTR __cdecl CJabberProto::OnMenuLoadHistory(WPARAM hContact, LPARAM) -{ - if (hContact == 0 || !m_bEnableMam) - return 0; - - // wipe out old history first - if (IDYES == MessageBoxW(NULL, TranslateT("Do you want to erase local history before loading it from server?"), m_tszUserName, MB_YESNOCANCEL | MB_ICONQUESTION)) { - DB::ECPTR pCursor(DB::Events(hContact)); - while (pCursor.FetchNext()) - pCursor.DeleteEvent(); - } - - // load remaining items from server - if (m_bJabberOnline) { - ptrA jid(getUStringA(hContact, "jid")); - if (jid != nullptr) { - m_bMamCreateRead = true; - MamSendForm(jid); - } - } - return 0; -} +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (c) 2007 Maxim Mluhov +Copyright (C) 2012-23 Miranda NG team + +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. + +*/ + +#include "stdafx.h" +#include "jabber_iq.h" +#include "jabber_caps.h" + +void CJabberProto::OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) +{ + if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { + if (auto *n = XmlFirstChild(iqNode, "prefs")) { + m_bMamPrefsAvailable = true; + + if (auto *type = n->Attribute("default")) { + if (!strcmp(type, "never")) + m_iMamMode = 0; + else if (!strcmp(type, "roster")) + m_iMamMode = 1; + else + m_iMamMode = 2; + } + } + } + + // shall we retrieve missing messages? + if (pInfo->GetUserData()) + MamRetrieveMissingMessages(); +} + +void CJabberProto::MamSetMode(int iNewMode) +{ + if (!m_bEnableMam) + return; + + const char *szMode; + switch (iNewMode) { + case 0: szMode = "never"; break; + case 1: szMode = "roster"; break; + default: szMode = "always"; break; + } + + XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultMamInfo, JABBER_IQ_TYPE_SET)); + auto *node = iq << XCHILDNS("prefs", JABBER_FEAT_MAM) << XATTR("default", szMode); + node << XCHILD("always"); node << XCHILD("never"); + m_ThreadInfo->send(iq); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CJabberProto::MamRetrieveMissingMessages() +{ + CMStringA szLastId = getMStringA("LastMamId"); + + XmlNodeIq iq("set", SerialNext()); + auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); + + if (szLastId.IsEmpty()) { + m_bMamDisableMessages = true; // our goal is to save message id, not to store messages + m_bMamCreateRead = false; + + char buf[100]; + time2str(time(0), buf, _countof(buf)); + + auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); + form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); + form << XCHILD("field") << XATTR("var", "end") << XCHILD("value", buf); + } + else { + auto *set = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); + set << XCHILD("max", "1000"); + set << XCHILD("after", szLastId); + } + + m_ThreadInfo->send(iq); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Contact's history loader + +void CJabberProto::MamSendForm(const char *pszWith, const char *pszAfter) +{ + auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET); + pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM); + + XmlNodeIq iq(pReq); + auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); + + auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); + form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); + if (pszWith != nullptr) + form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", pszWith); + + auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); + rsm << XCHILD("max", "1000"); + if (pszAfter != nullptr) + rsm << XCHILD("after", pszAfter); + m_ThreadInfo->send(iq); +} + + +void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) +{ + // even if that flag was enabled, unset it + m_bMamDisableMessages = false; + + if (auto *fin = XmlGetChildByTag(iqNode, "fin", "xmlns", JABBER_FEAT_MAM)) { + // if dataset is complete, there's nothing more to do + if (!mir_strcmp(XmlGetAttr(fin, "complete"), "true")) + return; + + if (auto *set = XmlGetChildByTag(fin, "set", "xmlns", "http://jabber.org/protocol/rsm")) + if (auto *lastId = XmlGetChildText(set, "last")) + MamSendForm(ptrA(getUStringA(pInfo->GetHContact(), "jid")), lastId); + } +} + +INT_PTR __cdecl CJabberProto::OnMenuLoadHistory(WPARAM hContact, LPARAM) +{ + if (hContact == 0 || !m_bEnableMam) + return 0; + + // wipe out old history first + if (IDYES == MessageBoxW(NULL, TranslateT("Do you want to erase local history before loading it from server?"), m_tszUserName, MB_YESNOCANCEL | MB_ICONQUESTION)) { + DB::ECPTR pCursor(DB::Events(hContact)); + while (pCursor.FetchNext()) + pCursor.DeleteEvent(); + } + + // load remaining items from server + if (m_bJabberOnline) { + ptrA jid(getUStringA(hContact, "jid")); + if (jid != nullptr) { + m_bMamCreateRead = true; + MamSendForm(jid); + } + } + return 0; +} diff --git a/protocols/JabberG/src/jabber_menu.cpp b/protocols/JabberG/src/jabber_menu.cpp index 920d390f1a..0d510f6859 100644 --- a/protocols/JabberG/src/jabber_menu.cpp +++ b/protocols/JabberG/src/jabber_menu.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_message_handlers.cpp b/protocols/JabberG/src/jabber_message_handlers.cpp index 4301a4a5c8..7610908dfe 100644 --- a/protocols/JabberG/src/jabber_message_handlers.cpp +++ b/protocols/JabberG/src/jabber_message_handlers.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_message_manager.cpp b/protocols/JabberG/src/jabber_message_manager.cpp index a794974a41..b1db1f299a 100644 --- a/protocols/JabberG/src/jabber_message_manager.cpp +++ b/protocols/JabberG/src/jabber_message_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_message_manager.h b/protocols/JabberG/src/jabber_message_manager.h index 07d381e90f..f7ae0d82ee 100644 --- a/protocols/JabberG/src/jabber_message_manager.h +++ b/protocols/JabberG/src/jabber_message_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_misc.cpp b/protocols/JabberG/src/jabber_misc.cpp index 88a6909eca..71115ef87a 100644 --- a/protocols/JabberG/src/jabber_misc.cpp +++ b/protocols/JabberG/src/jabber_misc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_notes.cpp b/protocols/JabberG/src/jabber_notes.cpp index 6734e7c26c..ac6272f5b7 100644 --- a/protocols/JabberG/src/jabber_notes.cpp +++ b/protocols/JabberG/src/jabber_notes.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_notes.h b/protocols/JabberG/src/jabber_notes.h index d103a938f4..bb7cc22181 100644 --- a/protocols/JabberG/src/jabber_notes.h +++ b/protocols/JabberG/src/jabber_notes.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_omemo.cpp b/protocols/JabberG/src/jabber_omemo.cpp index e5edb53d57..f51245d6bd 100644 --- a/protocols/JabberG/src/jabber_omemo.cpp +++ b/protocols/JabberG/src/jabber_omemo.cpp @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG -Copyright (c) 2017-22 Miranda NG team +Copyright (c) 2017-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_omemo.h b/protocols/JabberG/src/jabber_omemo.h index 8563735c36..94b9fd8dbb 100644 --- a/protocols/JabberG/src/jabber_omemo.h +++ b/protocols/JabberG/src/jabber_omemo.h @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG -Copyright (c) 2017-22 Miranda NG team +Copyright (c) 2017-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_opt.cpp b/protocols/JabberG/src/jabber_opt.cpp index c3f735c223..c7e85211cd 100644 --- a/protocols/JabberG/src/jabber_opt.cpp +++ b/protocols/JabberG/src/jabber_opt.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_password.cpp b/protocols/JabberG/src/jabber_password.cpp index d39c6a5aa5..0fe0700998 100644 --- a/protocols/JabberG/src/jabber_password.cpp +++ b/protocols/JabberG/src/jabber_password.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_presence_manager.cpp b/protocols/JabberG/src/jabber_presence_manager.cpp index de0dabbe65..873b2a1076 100644 --- a/protocols/JabberG/src/jabber_presence_manager.cpp +++ b/protocols/JabberG/src/jabber_presence_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_presence_manager.h b/protocols/JabberG/src/jabber_presence_manager.h index 4dab8a4991..0c488747c5 100644 --- a/protocols/JabberG/src/jabber_presence_manager.h +++ b/protocols/JabberG/src/jabber_presence_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_privacy.cpp b/protocols/JabberG/src/jabber_privacy.cpp index 8a02af9bba..b1087fde09 100644 --- a/protocols/JabberG/src/jabber_privacy.cpp +++ b/protocols/JabberG/src/jabber_privacy.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_privacy.h b/protocols/JabberG/src/jabber_privacy.h index 4ad6f4294a..d6918a5f1e 100644 --- a/protocols/JabberG/src/jabber_privacy.h +++ b/protocols/JabberG/src/jabber_privacy.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index 8e2f349cb6..3811becab3 100644 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index 29b05e25c0..6037019f46 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_rc.cpp b/protocols/JabberG/src/jabber_rc.cpp index 99e642d38d..a5bae9e944 100644 --- a/protocols/JabberG/src/jabber_rc.cpp +++ b/protocols/JabberG/src/jabber_rc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team XEP-0146 support for Miranda IM diff --git a/protocols/JabberG/src/jabber_rc.h b/protocols/JabberG/src/jabber_rc.h index 430261602d..7549723872 100644 --- a/protocols/JabberG/src/jabber_rc.h +++ b/protocols/JabberG/src/jabber_rc.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team XEP-0146 support for Miranda IM diff --git a/protocols/JabberG/src/jabber_roster.cpp b/protocols/JabberG/src/jabber_roster.cpp index 5072fe9ebb..03d9e9ffdd 100644 --- a/protocols/JabberG/src/jabber_roster.cpp +++ b/protocols/JabberG/src/jabber_roster.cpp @@ -1,551 +1,551 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team - -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. - -*/ - -#include "stdafx.h" - -#include - -////////////////////////////////////////////////////////////////////////// -// roster editor -// - -enum -{ - RRA_FILLLIST = 0, - RRA_SYNCROSTER, - RRA_SYNCDONE -}; - -struct ROSTEREDITDAT -{ - HWND hList; - int index; - int subindex; -}; - -static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel) -{ - if (!bCancel) { - int len = GetWindowTextLength(hEditor) + 1; - wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); - if (buff) { - GetWindowText(hEditor, buff, len); - ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff); - } - mir_free(buff); - } - DestroyWindow(hEditor); -} - -static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA); - if (!edat) return 0; - switch (msg) { - case WM_KEYDOWN: - switch (wParam) { - case VK_RETURN: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - case VK_ESCAPE: - _RosterItemEditEnd(hEditor, edat, TRUE); - return 0; - } - break; - - case WM_GETDLGCODE: - if (lParam) { - MSG *msg2 = (MSG*)lParam; - if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0; - if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0; - } - return DLGC_WANTMESSAGE; - - case WM_KILLFOCUS: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - - case WM_DESTROY: - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0); - free(edat); - return 0; - } - - return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam); -} - -static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN) - SetFocus(hList); - - if (msg == WM_LBUTTONDOWN) { - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hList, &pt); - - LVHITTESTINFO lvhti = { 0 }; - lvhti.pt = pt; - ListView_SubItemHitTest(hList, &lvhti); - if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) { - RECT rc; - wchar_t buff[260]; - ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc); - ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff)); - HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr); - SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0); - ShowWindow(hEditor, SW_SHOW); - SetWindowText(hEditor, buff); - ClientToScreen(hList, &pt); - ScreenToClient(hEditor, &pt); - mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); - mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); - - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT)); - mir_subclassWindow(hEditor, _RosterItemNewEditProc); - edat->hList = hList; - edat->index = lvhti.iItem; - edat->subindex = lvhti.iSubItem; - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat); - } - } - return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberRosterOptDlgProc - advanced options dialog procedure - -class CRosterEditorDlg : public CJabberDlgBase -{ - friend struct CJabberProto; - typedef CJabberDlgBase CSuper; - - uint8_t m_bRRAction; - BOOL m_bReadyToDownload = true; - BOOL m_bReadyToUpload = false; - - void _RosterSendRequest(uint8_t rrAction) - { - m_bRRAction = rrAction; - - m_proto->m_ThreadInfo->send( - XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET)) - << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER)); - } - - int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked) - { - Utf2T wszJid(jid); - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_STATE; - item.iItem = m_list.GetItemCount(); - item.pszText = wszJid; - - int index = m_list.InsertItem(&item); - if (index < 0) - return index; - - m_list.SetCheckState(index, bChecked); - - m_list.SetItemText(index, 1, Utf2T(nick)); - m_list.SetItemText(index, 2, Utf2T(group)); - m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr))); - return index; - } - - void _RosterListClear() - { - m_list.DeleteAllItems(); - while (m_list.GetColumnWidth(0) > 0) - m_list.DeleteColumn(0); - - LV_COLUMN column = { 0 }; - column.mask = LVCF_TEXT; - - column.pszText = TranslateT("JID"); - m_list.InsertColumn(1, &column); - - column.pszText = TranslateT("Nickname"); - m_list.InsertColumn(2, &column); - - column.pszText = TranslateT("Group"); - m_list.InsertColumn(3, &column); - - column.pszText = TranslateT("Subscription"); - m_list.InsertColumn(4, &column); - - RECT rc; - GetClientRect(m_list.GetHwnd(), &rc); - ResizeColumns(rc.right - rc.left); - } - - void ResizeColumns(int width) - { - m_list.SetColumnWidth(0, width * 40 / 100); - m_list.SetColumnWidth(1, width * 25 / 100); - m_list.SetColumnWidth(2, width * 20 / 100); - m_list.SetColumnWidth(3, width * 12 / 100); - } - - void OnChangeStatus() - { - int count = m_list.GetItemCount(); - btnDownload.Enable(m_proto->m_bJabberOnline); - btnUpload.Enable(count && m_proto->m_bJabberOnline); - btnExport.Enable(count > 0); - } - - CCtrlButton btnDownload, btnUpload, btnExport, btnImport; - CCtrlListView m_list; - -public: - CRosterEditorDlg(CJabberProto *m_proto) : - CSuper(m_proto, IDD_ROSTER_EDITOR), - m_list(this, IDC_ROSTER), - btnExport(this, IDC_EXPORT), - btnImport(this, IDC_IMPORT), - btnUpload(this, IDC_UPLOAD), - btnDownload(this, IDC_DOWNLOAD) - { - SetMinSize(550, 390); - - btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export); - btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import); - btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload); - btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download); - } - - bool OnInitDialog() override - { - SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName)); - - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS)); - - Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - - m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES); - mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc); - _RosterListClear(); - OnChangeStatus(); - return true; - } - - void OnDestroy() override - { - m_proto->m_hwndRosterEditor = nullptr; - Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - Window_FreeIcon_IcoLib(m_hwnd); - } - - int Resizer(UTILRESIZECONTROL *urc) override - { - switch (urc->wId) { - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - case IDC_ROSTER: - ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx); - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; - case IDC_DOWNLOAD: - case IDC_UPLOAD: - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - case IDC_EXPORT: - case IDC_IMPORT: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; - } - - void HandleNode(const TiXmlElement *node) - { - if (m_bRRAction == RRA_FILLLIST) { - _RosterListClear(); - auto *query = XmlFirstChild(node, "query"); - if (query == nullptr) return; - - for (auto *item : TiXmlFilter(query, "item")) { - const char *jid = XmlGetAttr(item, "jid"); - if (jid == nullptr) - continue; - - const char *name = XmlGetAttr(item, "name"); - const char *subscription = XmlGetAttr(item, "subscription"); - const char *group = XmlGetChildText(item, "group"); - _RosterInsertListItem(jid, name, group, subscription, true); - } - - // now it is require to process whole contact list to add not in roster contacts - for (auto &hContact : m_proto->AccContacts()) { - ptrW tszJid(m_proto->getWStringA(hContact, "jid")); - if (tszJid == nullptr) - continue; - - LVFINDINFO lvfi = { 0 }; - lvfi.flags = LVFI_STRING; - lvfi.psz = tszJid; - if (m_list.FindItem(-1, &lvfi) == -1) { - ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle")); - ptrA tszGroup(db_get_utfa(hContact, "CList", "Group")); - _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false); - } - } - m_bReadyToDownload = false; - m_bReadyToUpload = true; - btnDownload.SetText(TranslateT("Download")); - btnUpload.SetText(TranslateT("Upload")); - OnChangeStatus(); - return; - } - - if (m_bRRAction == RRA_SYNCROSTER) { - btnUpload.SetText(TranslateT("Uploading...")); - auto *queryRoster = XmlFirstChild(node, "query"); - if (!queryRoster) - return; - - int ListItemCount = m_list.GetItemCount(); - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260]; - wchar_t group[260]; - wchar_t subscr[260]; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - - T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr); - auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid); - bool bRemove = !m_list.GetCheckState(index); - if (itemRoster && bRemove) { // delete item - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove"); - m_proto->m_ThreadInfo->send(iq); - } - else if (!bRemove) { - bool bPushed = itemRoster != 0; - const char *rosterName = 0, *rosterGroup = 0; - if (!bPushed) { - rosterName = XmlGetAttr(itemRoster, "name"); - if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName)) - bPushed = true; - if (!bPushed) { - auto *szSub = XmlGetAttr(itemRoster, "subscription"); - if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr)) - bPushed = true; - } - if (!bPushed) { - rosterGroup = XmlGetChildText(itemRoster, "group"); - if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup)) - bPushed = true; - } - } - if (bPushed) { - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item"); - if (rosterGroup || mir_strlen(szGroup)) - item << XCHILD("group", szGroup); - if (rosterName || mir_strlen(szName)) - item << XATTR("name", szName); - item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none"); - m_proto->m_ThreadInfo->send(iq); - } - } - } - m_bRRAction = RRA_SYNCDONE; - _RosterSendRequest(RRA_FILLLIST); - return; - } - - btnUpload.SetText(TranslateT("Upload")); - onClick_Download(nullptr); - }; - - void onClick_Download(CCtrlButton*) - { - m_bReadyToUpload = m_bReadyToDownload = false; - OnChangeStatus(); - btnDownload.SetText(TranslateT("Downloading...")); - _RosterSendRequest(RRA_FILLLIST); - } - - void onClick_Upload(CCtrlButton*) - { - m_bReadyToUpload = false; - OnChangeStatus(); - btnUpload.SetText(TranslateT("Connecting...")); - _RosterSendRequest(RRA_SYNCROSTER); - } - - void onClick_Export(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = { 0 }; - - wchar_t filter[MAX_PATH]; - mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0); - OPENFILENAME ofn = {}; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = filter; - ofn.lpstrFile = filename; - ofn.Flags = OFN_HIDEREADONLY; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetSaveFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"wb"); - if (!fp) - return; - - int ListItemCount = m_list.GetItemCount(); - - XmlNode root("Roster"); - - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260] = L""; - wchar_t group[260] = L""; - wchar_t subscr[260] = L""; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr)); - } - - tinyxml2::XMLPrinter printer(fp); - root.Print(&printer); - fclose(fp); - } - - void onClick_Import(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = {}; - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.hInstance = nullptr; - ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0"; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - ofn.lpstrFile = filename; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetOpenFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"rb"); - if (!fp) - return; - - TiXmlDocument doc; - int ret = doc.LoadFile(fp); - fclose(fp); - if (ret != 0) - return; - - _RosterListClear(); - - const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement(); - if (Table) { - for (auto *Row : TiXmlFilter(Table, "Row")) { - bool bAdd = false; - const char *jid = nullptr; - const char *name = nullptr; - const char *group = nullptr; - const char *subscr = nullptr; - auto *Cell = XmlFirstChild(Row, "Cell"); - auto *Data = XmlFirstChild(Cell, "Data"); - if (Data) { - if (!mir_strcmpi(Data->GetText(), "+")) - bAdd = true; - else if (mir_strcmpi(Data->GetText(), "-")) - continue; - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) { - jid = Data->GetText(); - if (!jid || mir_strlen(jid) == 0) - continue; - } - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) name = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) group = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) subscr = Data->GetText(); - } - _RosterInsertListItem(jid, name, group, subscr, bAdd); - } - } - else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) { - for (auto *Row : TiXmlFilter(Table, "Item")) { - auto *jid = Row->Attribute("jid"); - auto *name = Row->Attribute("name"); - auto *group = Row->Attribute("group"); - auto *subscr = Row->Attribute("subscription"); - _RosterInsertListItem(jid, name, group, subscr, true); - } - } - - OnChangeStatus(); - } -}; - -INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM) -{ - if (m_hwndRosterEditor) - SetForegroundWindow(m_hwndRosterEditor->GetHwnd()); - else { - m_hwndRosterEditor = new CRosterEditorDlg(this); - m_hwndRosterEditor->Show(); - } - - return 0; -} - -void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->HandleNode(node); -} - -void CJabberProto::JabberUpdateDialogs(BOOL) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->OnChangeStatus(); -} +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (C) 2012-23 Miranda NG team + +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. + +*/ + +#include "stdafx.h" + +#include + +////////////////////////////////////////////////////////////////////////// +// roster editor +// + +enum +{ + RRA_FILLLIST = 0, + RRA_SYNCROSTER, + RRA_SYNCDONE +}; + +struct ROSTEREDITDAT +{ + HWND hList; + int index; + int subindex; +}; + +static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel) +{ + if (!bCancel) { + int len = GetWindowTextLength(hEditor) + 1; + wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); + if (buff) { + GetWindowText(hEditor, buff, len); + ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff); + } + mir_free(buff); + } + DestroyWindow(hEditor); +} + +static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA); + if (!edat) return 0; + switch (msg) { + case WM_KEYDOWN: + switch (wParam) { + case VK_RETURN: + _RosterItemEditEnd(hEditor, edat, FALSE); + return 0; + case VK_ESCAPE: + _RosterItemEditEnd(hEditor, edat, TRUE); + return 0; + } + break; + + case WM_GETDLGCODE: + if (lParam) { + MSG *msg2 = (MSG*)lParam; + if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0; + if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0; + } + return DLGC_WANTMESSAGE; + + case WM_KILLFOCUS: + _RosterItemEditEnd(hEditor, edat, FALSE); + return 0; + + case WM_DESTROY: + SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0); + free(edat); + return 0; + } + + return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam); +} + +static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN) + SetFocus(hList); + + if (msg == WM_LBUTTONDOWN) { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hList, &pt); + + LVHITTESTINFO lvhti = { 0 }; + lvhti.pt = pt; + ListView_SubItemHitTest(hList, &lvhti); + if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) { + RECT rc; + wchar_t buff[260]; + ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc); + ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff)); + HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr); + SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0); + ShowWindow(hEditor, SW_SHOW); + SetWindowText(hEditor, buff); + ClientToScreen(hList, &pt); + ScreenToClient(hEditor, &pt); + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + + ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT)); + mir_subclassWindow(hEditor, _RosterItemNewEditProc); + edat->hList = hList; + edat->index = lvhti.iItem; + edat->subindex = lvhti.iSubItem; + SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat); + } + } + return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberRosterOptDlgProc - advanced options dialog procedure + +class CRosterEditorDlg : public CJabberDlgBase +{ + friend struct CJabberProto; + typedef CJabberDlgBase CSuper; + + uint8_t m_bRRAction; + BOOL m_bReadyToDownload = true; + BOOL m_bReadyToUpload = false; + + void _RosterSendRequest(uint8_t rrAction) + { + m_bRRAction = rrAction; + + m_proto->m_ThreadInfo->send( + XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET)) + << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER)); + } + + int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked) + { + Utf2T wszJid(jid); + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_STATE; + item.iItem = m_list.GetItemCount(); + item.pszText = wszJid; + + int index = m_list.InsertItem(&item); + if (index < 0) + return index; + + m_list.SetCheckState(index, bChecked); + + m_list.SetItemText(index, 1, Utf2T(nick)); + m_list.SetItemText(index, 2, Utf2T(group)); + m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr))); + return index; + } + + void _RosterListClear() + { + m_list.DeleteAllItems(); + while (m_list.GetColumnWidth(0) > 0) + m_list.DeleteColumn(0); + + LV_COLUMN column = { 0 }; + column.mask = LVCF_TEXT; + + column.pszText = TranslateT("JID"); + m_list.InsertColumn(1, &column); + + column.pszText = TranslateT("Nickname"); + m_list.InsertColumn(2, &column); + + column.pszText = TranslateT("Group"); + m_list.InsertColumn(3, &column); + + column.pszText = TranslateT("Subscription"); + m_list.InsertColumn(4, &column); + + RECT rc; + GetClientRect(m_list.GetHwnd(), &rc); + ResizeColumns(rc.right - rc.left); + } + + void ResizeColumns(int width) + { + m_list.SetColumnWidth(0, width * 40 / 100); + m_list.SetColumnWidth(1, width * 25 / 100); + m_list.SetColumnWidth(2, width * 20 / 100); + m_list.SetColumnWidth(3, width * 12 / 100); + } + + void OnChangeStatus() + { + int count = m_list.GetItemCount(); + btnDownload.Enable(m_proto->m_bJabberOnline); + btnUpload.Enable(count && m_proto->m_bJabberOnline); + btnExport.Enable(count > 0); + } + + CCtrlButton btnDownload, btnUpload, btnExport, btnImport; + CCtrlListView m_list; + +public: + CRosterEditorDlg(CJabberProto *m_proto) : + CSuper(m_proto, IDD_ROSTER_EDITOR), + m_list(this, IDC_ROSTER), + btnExport(this, IDC_EXPORT), + btnImport(this, IDC_IMPORT), + btnUpload(this, IDC_UPLOAD), + btnDownload(this, IDC_DOWNLOAD) + { + SetMinSize(550, 390); + + btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export); + btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import); + btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload); + btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download); + } + + bool OnInitDialog() override + { + SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName)); + + Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS)); + + Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); + + m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES); + mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc); + _RosterListClear(); + OnChangeStatus(); + return true; + } + + void OnDestroy() override + { + m_proto->m_hwndRosterEditor = nullptr; + Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); + Window_FreeIcon_IcoLib(m_hwnd); + } + + int Resizer(UTILRESIZECONTROL *urc) override + { + switch (urc->wId) { + case IDC_HEADERBAR: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; + case IDC_ROSTER: + ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx); + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; + case IDC_DOWNLOAD: + case IDC_UPLOAD: + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + case IDC_EXPORT: + case IDC_IMPORT: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; + } + + void HandleNode(const TiXmlElement *node) + { + if (m_bRRAction == RRA_FILLLIST) { + _RosterListClear(); + auto *query = XmlFirstChild(node, "query"); + if (query == nullptr) return; + + for (auto *item : TiXmlFilter(query, "item")) { + const char *jid = XmlGetAttr(item, "jid"); + if (jid == nullptr) + continue; + + const char *name = XmlGetAttr(item, "name"); + const char *subscription = XmlGetAttr(item, "subscription"); + const char *group = XmlGetChildText(item, "group"); + _RosterInsertListItem(jid, name, group, subscription, true); + } + + // now it is require to process whole contact list to add not in roster contacts + for (auto &hContact : m_proto->AccContacts()) { + ptrW tszJid(m_proto->getWStringA(hContact, "jid")); + if (tszJid == nullptr) + continue; + + LVFINDINFO lvfi = { 0 }; + lvfi.flags = LVFI_STRING; + lvfi.psz = tszJid; + if (m_list.FindItem(-1, &lvfi) == -1) { + ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle")); + ptrA tszGroup(db_get_utfa(hContact, "CList", "Group")); + _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false); + } + } + m_bReadyToDownload = false; + m_bReadyToUpload = true; + btnDownload.SetText(TranslateT("Download")); + btnUpload.SetText(TranslateT("Upload")); + OnChangeStatus(); + return; + } + + if (m_bRRAction == RRA_SYNCROSTER) { + btnUpload.SetText(TranslateT("Uploading...")); + auto *queryRoster = XmlFirstChild(node, "query"); + if (!queryRoster) + return; + + int ListItemCount = m_list.GetItemCount(); + for (int index = 0; index < ListItemCount; index++) { + wchar_t jid[JABBER_MAX_JID_LEN] = L""; + wchar_t name[260]; + wchar_t group[260]; + wchar_t subscr[260]; + m_list.GetItemText(index, 0, jid, _countof(jid)); + m_list.GetItemText(index, 1, name, _countof(name)); + m_list.GetItemText(index, 2, group, _countof(group)); + m_list.GetItemText(index, 3, subscr, _countof(subscr)); + + T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr); + auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid); + bool bRemove = !m_list.GetCheckState(index); + if (itemRoster && bRemove) { // delete item + XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); + iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove"); + m_proto->m_ThreadInfo->send(iq); + } + else if (!bRemove) { + bool bPushed = itemRoster != 0; + const char *rosterName = 0, *rosterGroup = 0; + if (!bPushed) { + rosterName = XmlGetAttr(itemRoster, "name"); + if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName)) + bPushed = true; + if (!bPushed) { + auto *szSub = XmlGetAttr(itemRoster, "subscription"); + if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr)) + bPushed = true; + } + if (!bPushed) { + rosterGroup = XmlGetChildText(itemRoster, "group"); + if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup)) + bPushed = true; + } + } + if (bPushed) { + XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); + auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item"); + if (rosterGroup || mir_strlen(szGroup)) + item << XCHILD("group", szGroup); + if (rosterName || mir_strlen(szName)) + item << XATTR("name", szName); + item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none"); + m_proto->m_ThreadInfo->send(iq); + } + } + } + m_bRRAction = RRA_SYNCDONE; + _RosterSendRequest(RRA_FILLLIST); + return; + } + + btnUpload.SetText(TranslateT("Upload")); + onClick_Download(nullptr); + }; + + void onClick_Download(CCtrlButton*) + { + m_bReadyToUpload = m_bReadyToDownload = false; + OnChangeStatus(); + btnDownload.SetText(TranslateT("Downloading...")); + _RosterSendRequest(RRA_FILLLIST); + } + + void onClick_Upload(CCtrlButton*) + { + m_bReadyToUpload = false; + OnChangeStatus(); + btnUpload.SetText(TranslateT("Connecting...")); + _RosterSendRequest(RRA_SYNCROSTER); + } + + void onClick_Export(CCtrlButton*) + { + wchar_t filename[MAX_PATH] = { 0 }; + + wchar_t filter[MAX_PATH]; + mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0); + OPENFILENAME ofn = {}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = filter; + ofn.lpstrFile = filename; + ofn.Flags = OFN_HIDEREADONLY; + ofn.nMaxFile = _countof(filename); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = L"xml"; + if (!GetSaveFileNameW(&ofn)) + return; + + FILE * fp = _wfopen(filename, L"wb"); + if (!fp) + return; + + int ListItemCount = m_list.GetItemCount(); + + XmlNode root("Roster"); + + for (int index = 0; index < ListItemCount; index++) { + wchar_t jid[JABBER_MAX_JID_LEN] = L""; + wchar_t name[260] = L""; + wchar_t group[260] = L""; + wchar_t subscr[260] = L""; + m_list.GetItemText(index, 0, jid, _countof(jid)); + m_list.GetItemText(index, 1, name, _countof(name)); + m_list.GetItemText(index, 2, group, _countof(group)); + m_list.GetItemText(index, 3, subscr, _countof(subscr)); + root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr)); + } + + tinyxml2::XMLPrinter printer(fp); + root.Print(&printer); + fclose(fp); + } + + void onClick_Import(CCtrlButton*) + { + wchar_t filename[MAX_PATH] = {}; + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.hInstance = nullptr; + ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrFile = filename; + ofn.nMaxFile = _countof(filename); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = L"xml"; + if (!GetOpenFileNameW(&ofn)) + return; + + FILE * fp = _wfopen(filename, L"rb"); + if (!fp) + return; + + TiXmlDocument doc; + int ret = doc.LoadFile(fp); + fclose(fp); + if (ret != 0) + return; + + _RosterListClear(); + + const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement(); + if (Table) { + for (auto *Row : TiXmlFilter(Table, "Row")) { + bool bAdd = false; + const char *jid = nullptr; + const char *name = nullptr; + const char *group = nullptr; + const char *subscr = nullptr; + auto *Cell = XmlFirstChild(Row, "Cell"); + auto *Data = XmlFirstChild(Cell, "Data"); + if (Data) { + if (!mir_strcmpi(Data->GetText(), "+")) + bAdd = true; + else if (mir_strcmpi(Data->GetText(), "-")) + continue; + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) { + jid = Data->GetText(); + if (!jid || mir_strlen(jid) == 0) + continue; + } + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) name = Data->GetText(); + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) group = Data->GetText(); + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) subscr = Data->GetText(); + } + _RosterInsertListItem(jid, name, group, subscr, bAdd); + } + } + else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) { + for (auto *Row : TiXmlFilter(Table, "Item")) { + auto *jid = Row->Attribute("jid"); + auto *name = Row->Attribute("name"); + auto *group = Row->Attribute("group"); + auto *subscr = Row->Attribute("subscription"); + _RosterInsertListItem(jid, name, group, subscr, true); + } + } + + OnChangeStatus(); + } +}; + +INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM) +{ + if (m_hwndRosterEditor) + SetForegroundWindow(m_hwndRosterEditor->GetHwnd()); + else { + m_hwndRosterEditor = new CRosterEditorDlg(this); + m_hwndRosterEditor->Show(); + } + + return 0; +} + +void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*) +{ + if (m_hwndRosterEditor) + m_hwndRosterEditor->HandleNode(node); +} + +void CJabberProto::JabberUpdateDialogs(BOOL) +{ + if (m_hwndRosterEditor) + m_hwndRosterEditor->OnChangeStatus(); +} diff --git a/protocols/JabberG/src/jabber_search.cpp b/protocols/JabberG/src/jabber_search.cpp index 8beaf9fb7b..5102211399 100644 --- a/protocols/JabberG/src/jabber_search.cpp +++ b/protocols/JabberG/src/jabber_search.cpp @@ -1,765 +1,765 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Artem Shpynov -Copyright (C) 2012-22 Miranda NG team - -Module implements a search according to XEP-0055: Jabber Search -http://www.xmpp.org/extensions/xep-0055.html - -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. - -*/ - -#include "stdafx.h" -#include -#include "jabber_iq.h" -#include "jabber_caps.h" - -/////////////////////////////////////////////////////////////////////////////// -// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling - -static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_COMMAND && lParam != 0) { - HWND hwndDlg = GetParent(hwnd); - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (dat && lParam) { - int pos = dat->curPos; - RECT MineRect; - RECT FrameRect; - GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &FrameRect); - GetWindowRect((HWND)lParam, &MineRect); - if (MineRect.top - 10 < FrameRect.top) { - pos = dat->curPos + (MineRect.top - 14 - FrameRect.top); - if (pos < 0) pos = 0; - } - else if (MineRect.bottom > FrameRect.bottom) { - pos = dat->curPos + (MineRect.bottom - FrameRect.bottom); - if (dat->frameHeight + pos > dat->CurrentHeight) - pos = dat->CurrentHeight - dat->frameHeight; - } - if (pos != dat->curPos) { - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); - RECT Invalid = dat->frameRect; - if (dat->curPos - pos > 0) - Invalid.bottom = Invalid.top + (dat->curPos - pos); - else - Invalid.top = Invalid.bottom + (dat->curPos - pos); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - dat->curPos = pos; - } - } - - // Transmit focus set notification to parent window - if (HIWORD(wParam) == EN_SETFOCUS) - PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); - } - - if (msg == WM_PAINT) { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - FillRect(hdc, &(ps.rcPaint), GetSysColorBrush(COLOR_BTNFACE)); - EndPaint(hwnd, &ps); - } - - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -/////////////////////////////////////////////////////////////////////////////// -// Add Search field to form - -static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat) -{ - if (!FieldDat || !FieldDat->Label || !FieldDat->Var) - return FALSE; - - HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0); - HWND hwndParent = GetDlgItem(hwndDlg, IDC_FRAME); - LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE); - frameExStyle |= WS_EX_CONTROLPARENT; - SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FRAME), GWLP_WNDPROC, (LONG_PTR)JabberSearchFrameProc); - - int CornerX = 1; - int CornerY = 1; - RECT rect; - GetClientRect(hwndParent, &rect); - int width = rect.right - 5 - CornerX; - - int Order = (FieldDat->bHidden) ? -1 : FieldDat->Order; - - HWND hwndLabel = CreateWindowEx(0, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr); - HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr); - SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont, 0); - SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont, 0); - if (!FieldDat->bHidden) { - ShowWindow(hwndLabel, SW_SHOW); - ShowWindow(hwndVar, SW_SHOW); - EnableWindow(hwndLabel, !FieldDat->bReadOnly); - SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly, 0); - } - - // remade list - // reallocation - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (dat) { - dat->pJSInf = (JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount + 1)); - dat->pJSInf[dat->nJSInfCount].hwndCaptionItem = hwndLabel; - dat->pJSInf[dat->nJSInfCount].hwndValueItem = hwndVar; - dat->pJSInf[dat->nJSInfCount].szFieldCaption = wcsdup(FieldDat->Label); - dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var); - dat->nJSInfCount++; - } - return CornerY + Order * 40 + 14 + 20; -} - -//////////////////////////////////////////////////////////////////////////////// -// Available search field request result handler (XEP-0055. Examples 2, 7) - -void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*) -{ - if (!searchHandleDlg) - return; - - const char *type = XmlGetAttr(iqNode, "type"); - if (type == nullptr) - return; - - if (!mir_strcmp(type, "result")) { - auto *queryNode = XmlFirstChild(iqNode, "query"); - auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); - - ShowWindow(searchHandleDlg, SW_HIDE); - if (xNode) { - // 1. Form - PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0); - auto *xcNode = XmlFirstChild(xNode, "instructions"); - if (xcNode) - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText()); - } - else { - int Order = 0; - for (auto *chNode : TiXmlEnum(queryNode)) { - if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText()) - SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText()))); - else if (chNode->Name()) { - Data *MyData = (Data*)malloc(sizeof(Data)); - memset(MyData, 0, sizeof(Data)); - - MyData->Label = mir_utf8decodeW(chNode->Name()); - MyData->Var = mir_utf8decodeW(chNode->Name()); - MyData->defValue = mir_utf8decodeW(chNode->GetText()); - MyData->Order = Order; - if (MyData->defValue) - MyData->bReadOnly = true; - PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData); - Order++; - } - } - } - - const char *szFrom = XmlGetAttr(iqNode, "from"); - if (szFrom) - SearchAddToRecent(szFrom, searchHandleDlg); - PostMessage(searchHandleDlg, WM_USER + 10, 0, 0); - ShowWindow(searchHandleDlg, SW_SHOW); - } - else if (!mir_strcmp(type, "error")) { - const char *code = ""; - const char *description = ""; - auto *errorNode = XmlFirstChild(iqNode, "error"); - if (errorNode) { - code = XmlGetAttr(errorNode, "code"); - description = errorNode->GetText(); - } - - char buff[255]; - mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description); - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); - } - else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server")); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Return results to search dialog -// The pmFields is the pointer to map of Not unical but ordered -// This can help to made result parser routines more simple - -static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr }; - -static int TCharKeyCmp(const char *p1, const char *p2) -{ - return mir_strcmpi(p1, p2); -} - -static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST &plUsersInfo, UNIQUE_MAP &pmAllFields) -{ - LIST ListOfNonEmptyFields(20, TCharKeyCmp); - LIST ListOfFields(20); - - // lets fill the ListOfNonEmptyFields but in users order - for (auto &pmUserData : plUsersInfo) { - int nUserFields = pmUserData->getCount(); - for (int j = 0; j < nUserFields; j++) { - char *var = pmUserData->getKeyName(j); - if (var && ListOfNonEmptyFields.getIndex(var) < 0) - ListOfNonEmptyFields.insert(var); - } - } - - // now fill the ListOfFields but order is from pmAllFields - int nAllCount = pmAllFields.getCount(); - for (int i = 0; i < nAllCount; i++) { - char *var = pmAllFields.getUnOrderedKeyName(i); - if (var && ListOfNonEmptyFields.getIndex(var) < 0) - continue; - ListOfFields.insert(var); - } - - // now lets transfer field names - int nFieldCount = ListOfFields.getCount(); - - CUSTOMSEARCHRESULTS Results = { 0 }; - Results.nSize = sizeof(Results); - Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount); - Results.nFieldCount = nFieldCount; - - // Sending Columns Titles - for (int i = 0; i < nFieldCount; i++) { - char *var = ListOfFields[i]; - if (var) - Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]); - } - - Results.psr.cbSize = 0; // sending column names - ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); - for (int i = 0; i < nFieldCount; i++) - replaceStrW(Results.pszFields[i], nullptr); - - // Sending Users Data - Results.psr.cbSize = sizeof(Results.psr); - - for (auto &pmUserData : plUsersInfo) { - for (int j = 0; j < nFieldCount; j++) { - char *var = ListOfFields[j]; - char *value = pmUserData->operator [](var); - Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" "); - if (!mir_strcmpi(var, "jid") && value) - Results.psr.id.w = Results.pszFields[j]; - } - - const char *nick = nullptr; - for (int k = 0; k < _countof(nickfields) && !nick; k++) - nick = pmUserData->operator [](nickfields[k]); - - if (nick) { - Utf2T wszNick(nick); - wchar_t buff[200]; - if (mir_wstrcmpi(wszNick, Results.psr.id.w)) - mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w); - else - wcsncpy_s(buff, wszNick, _TRUNCATE); - - Results.psr.nick.w = buff; - } - else Results.psr.nick.w = L""; - Results.psr.flags = PSR_UNICODE; - - ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); - for (int i = 0; i < nFieldCount; i++) - replaceStrW(Results.pszFields[i], nullptr); - } - - mir_free(Results.pszFields); -} - -//////////////////////////////////////////////////////////////////////////////// -// Search field request result handler (XEP-0055. Examples 3, 8) - -void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*) -{ - const char *type; - int id; - - UNIQUE_MAP mColumnsNames(10); - LIST SearchResults(2); - - if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) { - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); - return; - } - - if (!mir_strcmp(type, "result")) { - auto *queryNode = XmlFirstChild(iqNode, "query"); - auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); - if (xNode) { - // 1. Form search results info - for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) { - auto *var = XmlGetAttr(fieldNode, "var"); - if (var) { - auto *label = XmlGetAttr(fieldNode, "label"); - mColumnsNames.insert(var, (label != nullptr) ? label : var); - } - } - - for (auto *itemNode : TiXmlFilter(xNode, "item")) { - UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); - for (auto *fieldNode : TiXmlFilter(itemNode, "field")) { - if (auto *var = XmlGetAttr(fieldNode, "var")) { - if (auto *textNode = XmlFirstChild(fieldNode, "value")) { - if (!mColumnsNames[var]) - mColumnsNames.insert(var, var); - pUserColumn->insert(var, textNode->GetText()); - } - } - } - - SearchResults.insert(pUserColumn); - } - } - else { - // 2. Field list search results info - for (auto *itemNode : TiXmlFilter(queryNode, "item")) { - UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); - - auto *jid = XmlGetAttr(itemNode, "jid"); - char *keyReturned; - mColumnsNames.insertCopyKey("jid", "jid", &keyReturned); - mColumnsNames.insert("jid", keyReturned); - pUserColumn->insertCopyKey("jid", jid, nullptr); - - for (auto *child : TiXmlEnum(itemNode)) { - const char *szColumnName = child->Name(); - if (szColumnName) { - const char *pszChild = child->GetText(); - if (pszChild && *pszChild) { - mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned); - mColumnsNames.insert(szColumnName, keyReturned); - pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr); - } - } - } - - SearchResults.insert(pUserColumn); - } - } - } - else if (!mir_strcmp(type, "error")) { - const char *code = ""; - const char *description = ""; - auto *errorNode = XmlFirstChild(iqNode, "error"); - if (errorNode) { - code = XmlGetAttr(errorNode, "code"); - description = errorNode->GetText(); - } - - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); - - char buff[255]; - mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description); - if (searchHandleDlg) - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); - else - MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP); - return; - } - - SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames); - - for (auto &it : SearchResults) - delete ((UNIQUE_MAP*)it); - - //send success to finish searching - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); -} - -static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM) -{ - DestroyWindow(hwnd); - return TRUE; -} - -static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat) -{ - if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) { - for (int i = 0; i < dat->nJSInfCount; i++) { - if (dat->pJSInf[i].hwndValueItem) - DestroyWindow(dat->pJSInf[i].hwndValueItem); - if (dat->pJSInf[i].hwndCaptionItem) - DestroyWindow(dat->pJSInf[i].hwndCaptionItem); - if (dat->pJSInf[i].szFieldCaption) - free(dat->pJSInf[i].szFieldCaption); - if (dat->pJSInf[i].szFieldName) - free(dat->pJSInf[i].szFieldName); - } - free(dat->pJSInf); - dat->pJSInf = nullptr; - } - else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0); - - SendDlgItemMessage(hwndDlg, IDC_FRAME, WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0), 0); - dat->nJSInfCount = 0; - ShowWindow(GetDlgItem(hwndDlg, IDC_VSCROLL), SW_HIDE); - SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, TranslateT("Select/type search service URL above and press ")); -} - -static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData *dat) -{ - HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME); - HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL); - RECT rc; - GetClientRect(hFrame, &rc); - GetClientRect(hFrame, &dat->frameRect); - dat->frameHeight = rc.bottom - rc.top; - if (dat->frameHeight < dat->CurrentHeight) { - ShowWindow(hwndScroll, SW_SHOW); - EnableWindow(hwndScroll, TRUE); - } - else ShowWindow(hwndScroll, SW_HIDE); - - SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight - dat->frameHeight, FALSE); -} - -int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData *dat) -{ - wchar_t szServerName[100]; - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE); - GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); - dat->CurrentHeight = 0; - dat->curPos = 0; - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); - - JabberSearchFreeData(hwndDlg, dat); - JabberSearchRefreshFrameScroll(hwndDlg, dat); - - SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server")); - - if (!m_bJabberOnline) - return 0; - - searchHandleDlg = hwndDlg; - - CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, T2Utf(szServerName)); - m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD)); - return pInfo->GetIqId(); -} - -static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr) -{ - int lResult = SendDlgItemMessage(hwndDlg, IDC_SERVER, (UINT)CB_FINDSTRING, 0, (LPARAM)szAddr); - if (lResult == -1) - SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr); -} - -void CJabberProto::SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB) -{ - // search in recent - for (int i = 0; i < 10; i++) { - char key[30]; - mir_snprintf(key, "RecentlySearched_%d", i); - ptrA szValue(getUStringA(key)); - if (szValue == nullptr || mir_strcmpi(szAddr, szValue)) - continue; - - for (int j = i; j < 10; j++) { - mir_snprintf(key, "RecentlySearched_%d", j + 1); - szValue = getUStringA(key); - if (szValue != nullptr) { - mir_snprintf(key, "RecentlySearched_%d", j); - setUString(0, key, szValue); - } - else { - if (deleteLastFromDB) { - mir_snprintf(key, "RecentlySearched_%d", j); - delSetting(0, key); - } - break; - } - } - break; - } -} - -void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog) -{ - char key[30]; - SearchDeleteFromRecent(szAddr, true); - - for (int j = 9; j > 0; j--) { - mir_snprintf(key, "RecentlySearched_%d", j - 1); - ptrW szValue(getWStringA(key)); - if (szValue != nullptr) { - mir_snprintf(key, "RecentlySearched_%d", j); - setWString(0, key, szValue); - } - } - - mir_snprintf(key, "RecentlySearched_%d", 0); - setUString(key, szAddr); - if (hwndDialog) - JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(szAddr)); -} - -static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - dat = new JabberSearchData(); - dat->ppro = (CJabberProto *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - /* Server Combo box */ - ptrA jud(dat->ppro->getStringA("Jud")); - if (jud != nullptr) { - SetDlgItemTextA(hwndDlg, IDC_SERVER, jud); - SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud); - } - - //TO DO: Add Transports here - for (auto &it : dat->ppro->m_lstTransports) - if (it != nullptr) - JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it)); - - for (int i = 0; i < 10; i++) { - char key[30]; - mir_snprintf(key, "RecentlySearched_%d", i); - ptrW szValue(dat->ppro->getWStringA(key)); - if (szValue != nullptr) - JabberSearchAddUrlToRecentCombo(hwndDlg, szValue); - } - - //TO DO: Add 4 recently used - dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg, dat); - } - return TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == IDC_SERVER) { - switch (HIWORD(wParam)) { - case CBN_SETFOCUS: - PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); - return TRUE; - - case CBN_EDITCHANGE: - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - return TRUE; - - case CBN_EDITUPDATE: - JabberSearchFreeData(hwndDlg, dat); - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - return TRUE; - - case CBN_SELENDOK: - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_GO, BN_CLICKED), 0); - return TRUE; - } - } - else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) { - dat->ppro->SearchRenewFields(hwndDlg, dat); - return TRUE; - } - break; - - case WM_SIZE: - { - //Resize IDC_FRAME to take full size - RECT rcForm; - GetWindowRect(hwndDlg, &rcForm); - RECT rcFrame; - GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame); - rcFrame.bottom = rcForm.bottom; - SetWindowPos(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, 0, 0, rcFrame.right - rcFrame.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); - GetWindowRect(GetDlgItem(hwndDlg, IDC_VSCROLL), &rcForm); - SetWindowPos(GetDlgItem(hwndDlg, IDC_VSCROLL), nullptr, 0, 0, rcForm.right - rcForm.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); - JabberSearchRefreshFrameScroll(hwndDlg, dat); - } - return TRUE; - - case WM_USER + 11: - { - dat->fSearchRequestIsXForm = TRUE; - if (dat->xNode) { - dat->doc.DeleteNode(dat->xNode); - dat->xNode = nullptr; - } - TiXmlElement *pNode = (TiXmlElement *)wParam; - if (pNode) { - dat->xNode = pNode->DeepClone(&dat->doc)->ToElement(); - JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight, TRUE); - } - ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW); - dat->nJSInfCount = 1; - } - return TRUE; - - case WM_USER + 10: - { - Data *MyDat = (Data *)lParam; - if (MyDat) { - dat->fSearchRequestIsXForm = (BOOL)wParam; - dat->CurrentHeight = JabberSearchAddField(hwndDlg, MyDat); - mir_free(MyDat->Label); - mir_free(MyDat->Var); - mir_free(MyDat->defValue); - free(MyDat); - } - else { - JabberSearchRefreshFrameScroll(hwndDlg, dat); - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); - dat->curPos = 0; - } - } - return TRUE; - - case WM_MOUSEWHEEL: - { - short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); - if (zDelta) { - int nScrollLines = 0; - SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void *)&nScrollLines, 0); - for (int i = 0; i < (nScrollLines + 1) / 2; i++) - SendMessage(hwndDlg, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0); - } - } - return TRUE; - - case WM_VSCROLL: - { - int pos; - if (dat != nullptr) { - pos = dat->curPos; - switch (LOWORD(wParam)) { - case SB_LINEDOWN: - pos += 10; - break; - case SB_LINEUP: - pos -= 10; - break; - case SB_PAGEDOWN: - pos += (dat->CurrentHeight - 10); - break; - case SB_PAGEUP: - pos -= (dat->CurrentHeight - 10); - break; - case SB_THUMBTRACK: - pos = HIWORD(wParam); - break; - } - if (pos > (dat->CurrentHeight - dat->frameHeight)) - pos = dat->CurrentHeight - dat->frameHeight; - if (pos < 0) - pos = 0; - if (dat->curPos != pos) { - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); - RECT Invalid = dat->frameRect; - if (dat->curPos - pos > 0) - Invalid.bottom = Invalid.top + (dat->curPos - pos); - else - Invalid.top = Invalid.bottom + (dat->curPos - pos); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN); - dat->curPos = pos; - } - } - } - return TRUE; - - case WM_DESTROY: - JabberSearchFreeData(hwndDlg, dat); - JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME)); - delete dat; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - return TRUE; - } - return FALSE; -} - -HWND CJabberProto::CreateExtendedSearchUI(HWND parent) -{ - if (parent && g_plugin.getInst()) { - ptrW szServer(getWStringA("LoginServer")); - if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms")) - return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this); - } - - return nullptr; // Failure -} - -////////////////////////////////////////////////////////////////////////// -// The function formats request to server - -HWND CJabberProto::SearchAdvanced(HWND hwndDlg) -{ - if (!m_bJabberOnline || !hwndDlg) - return nullptr; //error - - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (!dat) - return nullptr; //error - - // check if server connected (at least one field exists) - if (dat->nJSInfCount == 0) - return nullptr; - - // formating request - bool fRequestNotEmpty = false; - - // get server name - wchar_t szServerName[100]; - GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); - - // formating query - CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName)); - XmlNodeIq iq(pInfo); - TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD); - - if (m_tszSelectedLang) - iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :) - - // next can be 2 cases: - // Forms: XEP-0055 Example 7 - if (dat->fSearchRequestIsXForm) { - fRequestNotEmpty = true; - JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode); - } - else { //and Simple fields: XEP-0055 Example 3 - for (int i = 0; i < dat->nJSInfCount; i++) { - wchar_t szFieldValue[100]; - GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue)); - if (szFieldValue[0] != 0) { - XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get()); - fRequestNotEmpty = true; - } - } - } - - if (fRequestNotEmpty) { - m_ThreadInfo->send(iq); - return (HWND)pInfo->GetIqId(); - } - return nullptr; -} +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (c) 2007 Artem Shpynov +Copyright (C) 2012-23 Miranda NG team + +Module implements a search according to XEP-0055: Jabber Search +http://www.xmpp.org/extensions/xep-0055.html + +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. + +*/ + +#include "stdafx.h" +#include +#include "jabber_iq.h" +#include "jabber_caps.h" + +/////////////////////////////////////////////////////////////////////////////// +// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling + +static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_COMMAND && lParam != 0) { + HWND hwndDlg = GetParent(hwnd); + JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (dat && lParam) { + int pos = dat->curPos; + RECT MineRect; + RECT FrameRect; + GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &FrameRect); + GetWindowRect((HWND)lParam, &MineRect); + if (MineRect.top - 10 < FrameRect.top) { + pos = dat->curPos + (MineRect.top - 14 - FrameRect.top); + if (pos < 0) pos = 0; + } + else if (MineRect.bottom > FrameRect.bottom) { + pos = dat->curPos + (MineRect.bottom - FrameRect.bottom); + if (dat->frameHeight + pos > dat->CurrentHeight) + pos = dat->CurrentHeight - dat->frameHeight; + } + if (pos != dat->curPos) { + ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); + SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); + RECT Invalid = dat->frameRect; + if (dat->curPos - pos > 0) + Invalid.bottom = Invalid.top + (dat->curPos - pos); + else + Invalid.top = Invalid.bottom + (dat->curPos - pos); + + RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); + dat->curPos = pos; + } + } + + // Transmit focus set notification to parent window + if (HIWORD(wParam) == EN_SETFOCUS) + PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); + } + + if (msg == WM_PAINT) { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + FillRect(hdc, &(ps.rcPaint), GetSysColorBrush(COLOR_BTNFACE)); + EndPaint(hwnd, &ps); + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +/////////////////////////////////////////////////////////////////////////////// +// Add Search field to form + +static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat) +{ + if (!FieldDat || !FieldDat->Label || !FieldDat->Var) + return FALSE; + + HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0); + HWND hwndParent = GetDlgItem(hwndDlg, IDC_FRAME); + LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE); + frameExStyle |= WS_EX_CONTROLPARENT; + SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle); + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FRAME), GWLP_WNDPROC, (LONG_PTR)JabberSearchFrameProc); + + int CornerX = 1; + int CornerY = 1; + RECT rect; + GetClientRect(hwndParent, &rect); + int width = rect.right - 5 - CornerX; + + int Order = (FieldDat->bHidden) ? -1 : FieldDat->Order; + + HWND hwndLabel = CreateWindowEx(0, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr); + HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr); + SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont, 0); + SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont, 0); + if (!FieldDat->bHidden) { + ShowWindow(hwndLabel, SW_SHOW); + ShowWindow(hwndVar, SW_SHOW); + EnableWindow(hwndLabel, !FieldDat->bReadOnly); + SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly, 0); + } + + // remade list + // reallocation + JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (dat) { + dat->pJSInf = (JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount + 1)); + dat->pJSInf[dat->nJSInfCount].hwndCaptionItem = hwndLabel; + dat->pJSInf[dat->nJSInfCount].hwndValueItem = hwndVar; + dat->pJSInf[dat->nJSInfCount].szFieldCaption = wcsdup(FieldDat->Label); + dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var); + dat->nJSInfCount++; + } + return CornerY + Order * 40 + 14 + 20; +} + +//////////////////////////////////////////////////////////////////////////////// +// Available search field request result handler (XEP-0055. Examples 2, 7) + +void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*) +{ + if (!searchHandleDlg) + return; + + const char *type = XmlGetAttr(iqNode, "type"); + if (type == nullptr) + return; + + if (!mir_strcmp(type, "result")) { + auto *queryNode = XmlFirstChild(iqNode, "query"); + auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); + + ShowWindow(searchHandleDlg, SW_HIDE); + if (xNode) { + // 1. Form + PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0); + auto *xcNode = XmlFirstChild(xNode, "instructions"); + if (xcNode) + SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText()); + } + else { + int Order = 0; + for (auto *chNode : TiXmlEnum(queryNode)) { + if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText()) + SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText()))); + else if (chNode->Name()) { + Data *MyData = (Data*)malloc(sizeof(Data)); + memset(MyData, 0, sizeof(Data)); + + MyData->Label = mir_utf8decodeW(chNode->Name()); + MyData->Var = mir_utf8decodeW(chNode->Name()); + MyData->defValue = mir_utf8decodeW(chNode->GetText()); + MyData->Order = Order; + if (MyData->defValue) + MyData->bReadOnly = true; + PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData); + Order++; + } + } + } + + const char *szFrom = XmlGetAttr(iqNode, "from"); + if (szFrom) + SearchAddToRecent(szFrom, searchHandleDlg); + PostMessage(searchHandleDlg, WM_USER + 10, 0, 0); + ShowWindow(searchHandleDlg, SW_SHOW); + } + else if (!mir_strcmp(type, "error")) { + const char *code = ""; + const char *description = ""; + auto *errorNode = XmlFirstChild(iqNode, "error"); + if (errorNode) { + code = XmlGetAttr(errorNode, "code"); + description = errorNode->GetText(); + } + + char buff[255]; + mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description); + SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); + } + else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server")); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Return results to search dialog +// The pmFields is the pointer to map of Not unical but ordered +// This can help to made result parser routines more simple + +static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr }; + +static int TCharKeyCmp(const char *p1, const char *p2) +{ + return mir_strcmpi(p1, p2); +} + +static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST &plUsersInfo, UNIQUE_MAP &pmAllFields) +{ + LIST ListOfNonEmptyFields(20, TCharKeyCmp); + LIST ListOfFields(20); + + // lets fill the ListOfNonEmptyFields but in users order + for (auto &pmUserData : plUsersInfo) { + int nUserFields = pmUserData->getCount(); + for (int j = 0; j < nUserFields; j++) { + char *var = pmUserData->getKeyName(j); + if (var && ListOfNonEmptyFields.getIndex(var) < 0) + ListOfNonEmptyFields.insert(var); + } + } + + // now fill the ListOfFields but order is from pmAllFields + int nAllCount = pmAllFields.getCount(); + for (int i = 0; i < nAllCount; i++) { + char *var = pmAllFields.getUnOrderedKeyName(i); + if (var && ListOfNonEmptyFields.getIndex(var) < 0) + continue; + ListOfFields.insert(var); + } + + // now lets transfer field names + int nFieldCount = ListOfFields.getCount(); + + CUSTOMSEARCHRESULTS Results = { 0 }; + Results.nSize = sizeof(Results); + Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount); + Results.nFieldCount = nFieldCount; + + // Sending Columns Titles + for (int i = 0; i < nFieldCount; i++) { + char *var = ListOfFields[i]; + if (var) + Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]); + } + + Results.psr.cbSize = 0; // sending column names + ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); + for (int i = 0; i < nFieldCount; i++) + replaceStrW(Results.pszFields[i], nullptr); + + // Sending Users Data + Results.psr.cbSize = sizeof(Results.psr); + + for (auto &pmUserData : plUsersInfo) { + for (int j = 0; j < nFieldCount; j++) { + char *var = ListOfFields[j]; + char *value = pmUserData->operator [](var); + Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" "); + if (!mir_strcmpi(var, "jid") && value) + Results.psr.id.w = Results.pszFields[j]; + } + + const char *nick = nullptr; + for (int k = 0; k < _countof(nickfields) && !nick; k++) + nick = pmUserData->operator [](nickfields[k]); + + if (nick) { + Utf2T wszNick(nick); + wchar_t buff[200]; + if (mir_wstrcmpi(wszNick, Results.psr.id.w)) + mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w); + else + wcsncpy_s(buff, wszNick, _TRUNCATE); + + Results.psr.nick.w = buff; + } + else Results.psr.nick.w = L""; + Results.psr.flags = PSR_UNICODE; + + ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); + for (int i = 0; i < nFieldCount; i++) + replaceStrW(Results.pszFields[i], nullptr); + } + + mir_free(Results.pszFields); +} + +//////////////////////////////////////////////////////////////////////////////// +// Search field request result handler (XEP-0055. Examples 3, 8) + +void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*) +{ + const char *type; + int id; + + UNIQUE_MAP mColumnsNames(10); + LIST SearchResults(2); + + if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) { + ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); + return; + } + + if (!mir_strcmp(type, "result")) { + auto *queryNode = XmlFirstChild(iqNode, "query"); + auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); + if (xNode) { + // 1. Form search results info + for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) { + auto *var = XmlGetAttr(fieldNode, "var"); + if (var) { + auto *label = XmlGetAttr(fieldNode, "label"); + mColumnsNames.insert(var, (label != nullptr) ? label : var); + } + } + + for (auto *itemNode : TiXmlFilter(xNode, "item")) { + UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); + for (auto *fieldNode : TiXmlFilter(itemNode, "field")) { + if (auto *var = XmlGetAttr(fieldNode, "var")) { + if (auto *textNode = XmlFirstChild(fieldNode, "value")) { + if (!mColumnsNames[var]) + mColumnsNames.insert(var, var); + pUserColumn->insert(var, textNode->GetText()); + } + } + } + + SearchResults.insert(pUserColumn); + } + } + else { + // 2. Field list search results info + for (auto *itemNode : TiXmlFilter(queryNode, "item")) { + UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); + + auto *jid = XmlGetAttr(itemNode, "jid"); + char *keyReturned; + mColumnsNames.insertCopyKey("jid", "jid", &keyReturned); + mColumnsNames.insert("jid", keyReturned); + pUserColumn->insertCopyKey("jid", jid, nullptr); + + for (auto *child : TiXmlEnum(itemNode)) { + const char *szColumnName = child->Name(); + if (szColumnName) { + const char *pszChild = child->GetText(); + if (pszChild && *pszChild) { + mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned); + mColumnsNames.insert(szColumnName, keyReturned); + pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr); + } + } + } + + SearchResults.insert(pUserColumn); + } + } + } + else if (!mir_strcmp(type, "error")) { + const char *code = ""; + const char *description = ""; + auto *errorNode = XmlFirstChild(iqNode, "error"); + if (errorNode) { + code = XmlGetAttr(errorNode, "code"); + description = errorNode->GetText(); + } + + ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); + + char buff[255]; + mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description); + if (searchHandleDlg) + SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); + else + MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP); + return; + } + + SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames); + + for (auto &it : SearchResults) + delete ((UNIQUE_MAP*)it); + + //send success to finish searching + ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); +} + +static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM) +{ + DestroyWindow(hwnd); + return TRUE; +} + +static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat) +{ + if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) { + for (int i = 0; i < dat->nJSInfCount; i++) { + if (dat->pJSInf[i].hwndValueItem) + DestroyWindow(dat->pJSInf[i].hwndValueItem); + if (dat->pJSInf[i].hwndCaptionItem) + DestroyWindow(dat->pJSInf[i].hwndCaptionItem); + if (dat->pJSInf[i].szFieldCaption) + free(dat->pJSInf[i].szFieldCaption); + if (dat->pJSInf[i].szFieldName) + free(dat->pJSInf[i].szFieldName); + } + free(dat->pJSInf); + dat->pJSInf = nullptr; + } + else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0); + + SendDlgItemMessage(hwndDlg, IDC_FRAME, WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0), 0); + dat->nJSInfCount = 0; + ShowWindow(GetDlgItem(hwndDlg, IDC_VSCROLL), SW_HIDE); + SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, TranslateT("Select/type search service URL above and press ")); +} + +static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData *dat) +{ + HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME); + HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL); + RECT rc; + GetClientRect(hFrame, &rc); + GetClientRect(hFrame, &dat->frameRect); + dat->frameHeight = rc.bottom - rc.top; + if (dat->frameHeight < dat->CurrentHeight) { + ShowWindow(hwndScroll, SW_SHOW); + EnableWindow(hwndScroll, TRUE); + } + else ShowWindow(hwndScroll, SW_HIDE); + + SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight - dat->frameHeight, FALSE); +} + +int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData *dat) +{ + wchar_t szServerName[100]; + EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE); + GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); + dat->CurrentHeight = 0; + dat->curPos = 0; + SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); + + JabberSearchFreeData(hwndDlg, dat); + JabberSearchRefreshFrameScroll(hwndDlg, dat); + + SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server")); + + if (!m_bJabberOnline) + return 0; + + searchHandleDlg = hwndDlg; + + CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, T2Utf(szServerName)); + m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD)); + return pInfo->GetIqId(); +} + +static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr) +{ + int lResult = SendDlgItemMessage(hwndDlg, IDC_SERVER, (UINT)CB_FINDSTRING, 0, (LPARAM)szAddr); + if (lResult == -1) + SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr); +} + +void CJabberProto::SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB) +{ + // search in recent + for (int i = 0; i < 10; i++) { + char key[30]; + mir_snprintf(key, "RecentlySearched_%d", i); + ptrA szValue(getUStringA(key)); + if (szValue == nullptr || mir_strcmpi(szAddr, szValue)) + continue; + + for (int j = i; j < 10; j++) { + mir_snprintf(key, "RecentlySearched_%d", j + 1); + szValue = getUStringA(key); + if (szValue != nullptr) { + mir_snprintf(key, "RecentlySearched_%d", j); + setUString(0, key, szValue); + } + else { + if (deleteLastFromDB) { + mir_snprintf(key, "RecentlySearched_%d", j); + delSetting(0, key); + } + break; + } + } + break; + } +} + +void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog) +{ + char key[30]; + SearchDeleteFromRecent(szAddr, true); + + for (int j = 9; j > 0; j--) { + mir_snprintf(key, "RecentlySearched_%d", j - 1); + ptrW szValue(getWStringA(key)); + if (szValue != nullptr) { + mir_snprintf(key, "RecentlySearched_%d", j); + setWString(0, key, szValue); + } + } + + mir_snprintf(key, "RecentlySearched_%d", 0); + setUString(key, szAddr); + if (hwndDialog) + JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(szAddr)); +} + +static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + dat = new JabberSearchData(); + dat->ppro = (CJabberProto *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + /* Server Combo box */ + ptrA jud(dat->ppro->getStringA("Jud")); + if (jud != nullptr) { + SetDlgItemTextA(hwndDlg, IDC_SERVER, jud); + SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud); + } + + //TO DO: Add Transports here + for (auto &it : dat->ppro->m_lstTransports) + if (it != nullptr) + JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it)); + + for (int i = 0; i < 10; i++) { + char key[30]; + mir_snprintf(key, "RecentlySearched_%d", i); + ptrW szValue(dat->ppro->getWStringA(key)); + if (szValue != nullptr) + JabberSearchAddUrlToRecentCombo(hwndDlg, szValue); + } + + //TO DO: Add 4 recently used + dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg, dat); + } + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_SERVER) { + switch (HIWORD(wParam)) { + case CBN_SETFOCUS: + PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); + return TRUE; + + case CBN_EDITCHANGE: + EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); + return TRUE; + + case CBN_EDITUPDATE: + JabberSearchFreeData(hwndDlg, dat); + EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); + return TRUE; + + case CBN_SELENDOK: + EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); + PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_GO, BN_CLICKED), 0); + return TRUE; + } + } + else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) { + dat->ppro->SearchRenewFields(hwndDlg, dat); + return TRUE; + } + break; + + case WM_SIZE: + { + //Resize IDC_FRAME to take full size + RECT rcForm; + GetWindowRect(hwndDlg, &rcForm); + RECT rcFrame; + GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame); + rcFrame.bottom = rcForm.bottom; + SetWindowPos(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, 0, 0, rcFrame.right - rcFrame.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); + GetWindowRect(GetDlgItem(hwndDlg, IDC_VSCROLL), &rcForm); + SetWindowPos(GetDlgItem(hwndDlg, IDC_VSCROLL), nullptr, 0, 0, rcForm.right - rcForm.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); + JabberSearchRefreshFrameScroll(hwndDlg, dat); + } + return TRUE; + + case WM_USER + 11: + { + dat->fSearchRequestIsXForm = TRUE; + if (dat->xNode) { + dat->doc.DeleteNode(dat->xNode); + dat->xNode = nullptr; + } + TiXmlElement *pNode = (TiXmlElement *)wParam; + if (pNode) { + dat->xNode = pNode->DeepClone(&dat->doc)->ToElement(); + JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight, TRUE); + } + ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW); + dat->nJSInfCount = 1; + } + return TRUE; + + case WM_USER + 10: + { + Data *MyDat = (Data *)lParam; + if (MyDat) { + dat->fSearchRequestIsXForm = (BOOL)wParam; + dat->CurrentHeight = JabberSearchAddField(hwndDlg, MyDat); + mir_free(MyDat->Label); + mir_free(MyDat->Var); + mir_free(MyDat->defValue); + free(MyDat); + } + else { + JabberSearchRefreshFrameScroll(hwndDlg, dat); + ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, nullptr, &(dat->frameRect)); + SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); + dat->curPos = 0; + } + } + return TRUE; + + case WM_MOUSEWHEEL: + { + short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); + if (zDelta) { + int nScrollLines = 0; + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void *)&nScrollLines, 0); + for (int i = 0; i < (nScrollLines + 1) / 2; i++) + SendMessage(hwndDlg, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0); + } + } + return TRUE; + + case WM_VSCROLL: + { + int pos; + if (dat != nullptr) { + pos = dat->curPos; + switch (LOWORD(wParam)) { + case SB_LINEDOWN: + pos += 10; + break; + case SB_LINEUP: + pos -= 10; + break; + case SB_PAGEDOWN: + pos += (dat->CurrentHeight - 10); + break; + case SB_PAGEUP: + pos -= (dat->CurrentHeight - 10); + break; + case SB_THUMBTRACK: + pos = HIWORD(wParam); + break; + } + if (pos > (dat->CurrentHeight - dat->frameHeight)) + pos = dat->CurrentHeight - dat->frameHeight; + if (pos < 0) + pos = 0; + if (dat->curPos != pos) { + ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); + SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); + RECT Invalid = dat->frameRect; + if (dat->curPos - pos > 0) + Invalid.bottom = Invalid.top + (dat->curPos - pos); + else + Invalid.top = Invalid.bottom + (dat->curPos - pos); + + RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN); + dat->curPos = pos; + } + } + } + return TRUE; + + case WM_DESTROY: + JabberSearchFreeData(hwndDlg, dat); + JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME)); + delete dat; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + return TRUE; + } + return FALSE; +} + +HWND CJabberProto::CreateExtendedSearchUI(HWND parent) +{ + if (parent && g_plugin.getInst()) { + ptrW szServer(getWStringA("LoginServer")); + if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms")) + return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this); + } + + return nullptr; // Failure +} + +////////////////////////////////////////////////////////////////////////// +// The function formats request to server + +HWND CJabberProto::SearchAdvanced(HWND hwndDlg) +{ + if (!m_bJabberOnline || !hwndDlg) + return nullptr; //error + + JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (!dat) + return nullptr; //error + + // check if server connected (at least one field exists) + if (dat->nJSInfCount == 0) + return nullptr; + + // formating request + bool fRequestNotEmpty = false; + + // get server name + wchar_t szServerName[100]; + GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); + + // formating query + CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName)); + XmlNodeIq iq(pInfo); + TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD); + + if (m_tszSelectedLang) + iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :) + + // next can be 2 cases: + // Forms: XEP-0055 Example 7 + if (dat->fSearchRequestIsXForm) { + fRequestNotEmpty = true; + JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode); + } + else { //and Simple fields: XEP-0055 Example 3 + for (int i = 0; i < dat->nJSInfCount; i++) { + wchar_t szFieldValue[100]; + GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue)); + if (szFieldValue[0] != 0) { + XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get()); + fRequestNotEmpty = true; + } + } + } + + if (fRequestNotEmpty) { + m_ThreadInfo->send(iq); + return (HWND)pInfo->GetIqId(); + } + return nullptr; +} diff --git a/protocols/JabberG/src/jabber_search.h b/protocols/JabberG/src/jabber_search.h index 4916afb29a..3bfaa2df8a 100644 --- a/protocols/JabberG/src/jabber_search.h +++ b/protocols/JabberG/src/jabber_search.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Artem Shpynov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team Module implements a search according to XEP-0055: Jabber Search http://www.xmpp.org/extensions/xep-0055.html diff --git a/protocols/JabberG/src/jabber_secur.cpp b/protocols/JabberG/src/jabber_secur.cpp index a1cf3faccc..853016823d 100644 --- a/protocols/JabberG/src/jabber_secur.cpp +++ b/protocols/JabberG/src/jabber_secur.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_secur.h b/protocols/JabberG/src/jabber_secur.h index 133d55e840..90ad39bd60 100644 --- a/protocols/JabberG/src/jabber_secur.h +++ b/protocols/JabberG/src/jabber_secur.h @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_send_manager.cpp b/protocols/JabberG/src/jabber_send_manager.cpp index c70d4017af..67031c0567 100644 --- a/protocols/JabberG/src/jabber_send_manager.cpp +++ b/protocols/JabberG/src/jabber_send_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_send_manager.h b/protocols/JabberG/src/jabber_send_manager.h index d223782694..9b98db0458 100644 --- a/protocols/JabberG/src/jabber_send_manager.h +++ b/protocols/JabberG/src/jabber_send_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan Copyright (c) 2007 Maxim Mluhov Copyright (c) 2008-09 Dmitriy Chervov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_strm_mgmt.cpp b/protocols/JabberG/src/jabber_strm_mgmt.cpp index e0f95eb2a8..a081570cfe 100644 --- a/protocols/JabberG/src/jabber_strm_mgmt.cpp +++ b/protocols/JabberG/src/jabber_strm_mgmt.cpp @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG -Copyright (c) 2018-22 Miranda NG team +Copyright (c) 2018-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_strm_mgmt.h b/protocols/JabberG/src/jabber_strm_mgmt.h index 412127d1d2..158c72fefb 100644 --- a/protocols/JabberG/src/jabber_strm_mgmt.h +++ b/protocols/JabberG/src/jabber_strm_mgmt.h @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG -Copyright (c) 2018-22 Miranda NG team +Copyright (c) 2018-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_svc.cpp b/protocols/JabberG/src/jabber_svc.cpp index 63fbd74e8b..5252c0865f 100644 --- a/protocols/JabberG/src/jabber_svc.cpp +++ b/protocols/JabberG/src/jabber_svc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index 212c6bdd6a..0ad67e79dd 100644 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_treelist.cpp b/protocols/JabberG/src/jabber_treelist.cpp index 21fcb1fbc5..35467cbd1c 100644 --- a/protocols/JabberG/src/jabber_treelist.cpp +++ b/protocols/JabberG/src/jabber_treelist.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_userinfo.cpp b/protocols/JabberG/src/jabber_userinfo.cpp index f164252fbd..0f9271481a 100644 --- a/protocols/JabberG/src/jabber_userinfo.cpp +++ b/protocols/JabberG/src/jabber_userinfo.cpp @@ -1,912 +1,912 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team - -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. - -*/ - -#include "stdafx.h" - -#include -#include -#include - -#include "jabber_list.h" - -static MWindowList hUserInfoList = nullptr; - -class JabberBaseUserInfoDlg : public CUserInfoPageDlg -{ - INT_PTR DoRefresh(UINT, WPARAM, LPARAM) - { - OnRefresh(); - return 0; - } - -protected: - UI_MESSAGE_MAP(JabberBaseUserInfoDlg, CUserInfoPageDlg); - UI_MESSAGE(WM_PROTO_REFRESH, DoRefresh); - UI_MESSAGE(WM_JABBER_REFRESH_VCARD, DoRefresh); - UI_MESSAGE_MAP_END(); - - CJabberProto *ppro; - - JabberBaseUserInfoDlg(CJabberProto *_ppro, int dlgId) : - CUserInfoPageDlg(g_plugin, dlgId), - ppro(_ppro) - {} -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoDlgProc - main user info dialog - -enum -{ - INFOLINE_DELETE = 0x80000000, - INFOLINE_MASK = 0x7fffffff, - INFOLINE_BAD_ID = 0x7fffffff, - - INFOLINE_NAME = 1, - INFOLINE_MOOD, - INFOLINE_ACTIVITY, - INFOLINE_TUNE, - INFOLINE_OFFLINE, - INFOLINE_MESSAGE, - INFOLINE_SOFTWARE, - INFOLINE_VERSION, - INFOLINE_SYSTEM, - INFOLINE_PRIORITY, - INFOLINE_IDLE, - INFOLINE_CAPS, - INFOLINE_SOFTWARE_INFORMATION, - INFOLINE_SUBSCRIPTION, - INFOLINE_LOGOFF, - INFOLINE_LOGOFF_MSG, - INFOLINE_LASTACTIVE, -}; - -__forceinline uint32_t sttInfoLineId(uint32_t res, uint32_t type, uint32_t line = 0) -{ - return - (type << 24) & 0x7f000000 | - (res << 12) & 0x00fff000 | - (line) & 0x00000fff; -} - -class JabberUserInfoDlg : public JabberBaseUserInfoDlg -{ - JABBER_LIST_ITEM *item = nullptr; - int resourcesCount = -1; - - CCtrlTreeView m_tree; - - UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); - UI_MESSAGE(WM_PROTO_CHECK_ONLINE, OnCheckOnline); - UI_MESSAGE_MAP_END(); - - INT_PTR OnCheckOnline(UINT, WPARAM, LPARAM) - { - if (!ppro->m_bJabberOnline) - item = nullptr; - else - OnRefresh(); - return 0; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // User information block - - void CleanupInfo(int stage) - { - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - - switch (stage) { - case 0: - tvi.lParam |= INFOLINE_DELETE; - m_tree.SetItem(&tvi); - break; - - case 1: - if (tvi.lParam & INFOLINE_DELETE) { - hItem = m_tree.GetNextSibling(hItem); - m_tree.DeleteItem(tvi.hItem); - continue; - } - break; - } - - HTREEITEM hItemTmp = nullptr; - if (hItemTmp = m_tree.GetChild(hItem)) - hItem = hItemTmp; - else if (hItemTmp = m_tree.GetNextSibling(hItem)) - hItem = hItemTmp; - else { - while (true) { - if (!(hItem = m_tree.GetParent(hItem))) break; - if (hItemTmp = m_tree.GetNextSibling(hItem)) { - hItem = hItemTmp; - break; - } - } - } - } - } - - HTREEITEM FindInfoLine(HTREEITEM htiRoot, LPARAM id = INFOLINE_BAD_ID) - { - if (id == INFOLINE_BAD_ID) return nullptr; - for (HTREEITEM hti = m_tree.GetChild(htiRoot); hti; hti = m_tree.GetNextSibling(hti)) { - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hti; - m_tree.GetItem(&tvi); - if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK)) - return hti; - } - return nullptr; - } - - HTREEITEM FillInfoLine(HTREEITEM htiRoot, HICON hIcon, const wchar_t *title, const char *value, LPARAM id = INFOLINE_BAD_ID, bool expand = false) - { - HTREEITEM hti = FindInfoLine(htiRoot, id); - - Utf2T wszValue(value); - const wchar_t *pwszValue = (value == nullptr) ? TranslateT("") : wszValue; - wchar_t buf[256]; - if (title) - mir_snwprintf(buf, L"%s: %s", title, pwszValue); - else - mir_wstrncpy(buf, pwszValue, _countof(buf)); - - TVINSERTSTRUCT tvis = {}; - tvis.hParent = htiRoot; - tvis.hInsertAfter = TVI_LAST; - tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM; - tvis.itemex.pszText = buf; - tvis.itemex.lParam = id; - - if (hIcon) { - HIMAGELIST himl = m_tree.GetImageList(TVSIL_NORMAL); - tvis.itemex.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.itemex.iImage = - tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon); - IcoLib_ReleaseIcon(hIcon); - } - - if (hti) { - tvis.itemex.mask |= TVIF_HANDLE; - tvis.itemex.hItem = hti; - m_tree.SetItem(&tvis.itemex); - } - else { - tvis.itemex.mask |= TVIF_STATE; - tvis.itemex.stateMask = TVIS_EXPANDED; - tvis.itemex.state = expand ? TVIS_EXPANDED : 0; - hti = m_tree.InsertItem(&tvis); - } - - return hti; - } - - void FillAdvStatusInfo(HTREEITEM htiRoot, uint32_t dwInfoLine, MCONTACT hContact, wchar_t *szTitle, char *pszSlot) - { - ptrA szAdvStatusIcon(ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON)); - ptrW szAdvStatusTitle(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE)); - ptrW szAdvStatusText(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT)); - - if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) { - wchar_t szText[2048]; - if (szAdvStatusText && *szAdvStatusText) - mir_snwprintf(szText, L"%s (%s)", TranslateW(szAdvStatusTitle), szAdvStatusText.get()); - else - wcsncpy_s(szText, TranslateW(szAdvStatusTitle), _TRUNCATE); - FillInfoLine(htiRoot, IcoLib_GetIcon(szAdvStatusIcon), szTitle, T2Utf(szText), dwInfoLine); - } - } - - void FillResourceInfo(HTREEITEM htiRoot, int resource) - { - HTREEITEM htiResource = htiRoot; - pResourceStatus r = resource ? item->arResources[resource - 1] : item->getTemp(); - - if (r->m_szResourceName && *r->m_szResourceName) - htiResource = FillInfoLine(htiRoot, Skin_LoadProtoIcon(ppro->m_szModuleName, r->m_iStatus), - TranslateT("Resource"), r->m_szResourceName, sttInfoLineId(resource, INFOLINE_NAME), true); - - // StatusMsg - FillInfoLine(htiResource, nullptr /*Skin_LoadIcon(SKINICON_EVENT_MESSAGE)*/, - TranslateT("Message"), r->m_szStatusMessage, - sttInfoLineId(resource, INFOLINE_MESSAGE)); - - // Software - if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { - HICON hIcon = nullptr; - - if (ServiceExists(MS_FP_GETCLIENTICONT)) { - if (pCaps->GetSoft()) { - wchar_t buf[256]; - mir_snwprintf(buf, L"%s %s", pCaps->GetSoft(), pCaps->GetSoftVer()); - hIcon = Finger_GetClientIcon(buf, 0); - } - } - - FillInfoLine(htiResource, hIcon, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE)); - - // Version - FillInfoLine(htiResource, nullptr, TranslateT("Version"), pCaps->GetSoftMir() ? pCaps->GetSoftMir() : pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_VERSION)); - - // System - FillInfoLine(htiResource, nullptr, TranslateT("System"), pCaps->GetOsVer() ? pCaps->GetOsVer() : pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SYSTEM)); - - if (hIcon) - DestroyIcon(hIcon); - } - - // Resource priority - char buf[256]; - itoa(r->m_iPriority, buf, 10); - FillInfoLine(htiResource, nullptr, TranslateT("Resource priority"), buf, sttInfoLineId(resource, INFOLINE_PRIORITY)); - - // Idle - if (r->m_dwIdleStartTime != -1) { - if (r->m_dwIdleStartTime != 0) { - mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); - size_t len = mir_strlen(buf); - if (len > 0) - buf[len - 1] = 0; - } - else mir_strncpy(buf, TranslateU(""), _countof(buf)); - - FillInfoLine(htiResource, nullptr, TranslateT("Last activity"), buf, sttInfoLineId(resource, INFOLINE_IDLE)); - } - - // caps - JabberCapsBits jcb = ppro->GetResourceCapabilities(MakeJid(item->jid, r->m_szResourceName), r); - if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) { - HTREEITEM htiCaps = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS)); - int i; - for (i = 0; i < g_cJabberFeatCapPairs; i++) - if (jcb & g_JabberFeatCapPairs[i].jcbCap) { - char szDescription[1024]; - if (g_JabberFeatCapPairs[i].tszDescription) - mir_snprintf(szDescription, "%s (%s)", TranslateU(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature); - else - strncpy_s(szDescription, g_JabberFeatCapPairs[i].szFeature, _TRUNCATE); - FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i)); - } - - for (auto &it : ppro->m_lstJabberFeatCapPairsDynamic) { - if (jcb & it->jcbCap) { - char szDescription[1024]; - if (it->szDescription) - mir_snprintf(szDescription, "%s (%s)", TranslateU(it->szDescription), it->szFeature); - else - strncpy_s(szDescription, it->szFeature, _TRUNCATE); - FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i++)); - } - } - } - - // Software info - HTREEITEM htiSoftwareInfo = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION)); - int nLineId = 0; - if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { - if (pCaps->GetOs()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system"), pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetOsVer()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system version"), pCaps->GetOsVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoft()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoftVer()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software version"), pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoftMir()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Miranda core version"), pCaps->GetSoftMir(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - } - } - - void FillUserInfo() - { - m_tree.SendMsg(WM_SETREDRAW, FALSE, 0); - - CleanupInfo(0); - - HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", item->jid, sttInfoLineId(0, INFOLINE_NAME), true); - - if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) { - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD); - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY); - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE); - } - - // subscription - switch (item->subscription) { - case SUB_BOTH: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - case SUB_TO: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - case SUB_FROM: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - default: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - } - - // logoff - char buf[256]; - JABBER_RESOURCE_STATUS *r = item->getTemp(); - if (r->m_dwIdleStartTime != -1) { - if (r->m_dwIdleStartTime > 0) { - mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); - size_t len = mir_strlen(buf); - if (len > 0) - buf[len - 1] = 0; - } - else mir_strncpy(buf, TranslateU(""), _countof(buf)); - - FillInfoLine(htiRoot, nullptr, - (item->jid && strchr(item->jid, '@')) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf, - sttInfoLineId(0, INFOLINE_LOGOFF)); - } - - if (r->m_szStatusMessage) - FillInfoLine(htiRoot, nullptr, TranslateT("Logoff message"), r->m_szStatusMessage, sttInfoLineId(0, INFOLINE_LOGOFF_MSG)); - - // activity - if (item->m_pLastSeenResource) - mir_strncpy(buf, item->m_pLastSeenResource->m_szResourceName, _countof(buf)); - else - mir_strncpy(buf, TranslateU(""), _countof(buf)); - FillInfoLine(htiRoot, nullptr, TranslateT("Last active resource"), buf, sttInfoLineId(0, INFOLINE_LASTACTIVE)); - - // resources - if (item->arResources.getCount()) { - for (int i = 0; i < item->arResources.getCount(); i++) - FillResourceInfo(htiRoot, i + 1); - } - else if (!strchr(item->jid, '@') || (r->m_iStatus != ID_STATUS_OFFLINE)) - FillResourceInfo(htiRoot, 0); - - CleanupInfo(1); - m_tree.SendMsg(WM_SETREDRAW, TRUE, 0); - - RedrawWindow(m_tree.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE); - } - -public: - JabberUserInfoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_INFO_JABBER), - m_tree(this, IDC_TV_INFO) - { - m_tree.OnBuildMenu = Callback(this, &JabberUserInfoDlg::onMenu_Tree); - } - - bool OnInitDialog() override - { - ppro->WindowSubscribe(m_hwnd); - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_USERDETAILS); - - RECT rc; - GetClientRect(m_hwnd, &rc); - MoveWindow(m_tree.GetHwnd(), 5, 5, rc.right - 10, rc.bottom - 10, TRUE); - - HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); - ImageList_AddSkinIcon(himl, SKINICON_OTHER_SMALLDOT); - TreeView_SetImageList(m_tree.GetHwnd(), himl, TVSIL_NORMAL); - - WindowList_Add(hUserInfoList, m_hwnd, m_hContact); - return true; - } - - void OnDestroy() override - { - ppro->WindowUnsubscribe(m_hwnd); - WindowList_Remove(hUserInfoList, m_hwnd); - ImageList_Destroy(m_tree.SetImageList(nullptr, TVSIL_NORMAL)); - Window_FreeIcon_IcoLib(m_hwnd); - } - - int Resizer(UTILRESIZECONTROL*) override - { - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - } - - bool OnRefresh() override - { - if (item == nullptr) { - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid == nullptr) - return false; - - if (ppro->m_bJabberOnline) - if (!(item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid))) - item = ppro->ListGetItemPtr(LIST_ROSTER, jid); - - if (item == nullptr) { - m_tree.DeleteAllItems(); - HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", jid, sttInfoLineId(0, INFOLINE_NAME), true); - FillInfoLine(htiRoot, g_plugin.getIcon(IDI_VCARD), nullptr, TranslateU("Please switch online to see more details.")); - return false; - } - } - FillUserInfo(); - return false; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // Context menu - - void GetNodeText(HTREEITEM hti, CMStringW &buf, int indent = 0) - { - for (int i = 0; i < indent; i++) - buf.AppendChar('\t'); - - wchar_t wszText[256]; - TVITEMEX tvi = {}; - tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; - tvi.hItem = hti; - tvi.cchTextMax = _countof(wszText); - tvi.pszText = wszText; - if (!m_tree.GetItem(&tvi)) // failure, maybe item was removed... - return; - - buf.Append(wszText); - buf.Append(L"\r\n"); - - if (tvi.state & TVIS_EXPANDED) - for (hti = m_tree.GetChild(hti); hti; hti = m_tree.GetNextSibling(hti)) - GetNodeText(hti, buf, indent + 1); - } - - void onMenu_Tree(CContextMenuPos *pos) - { - if (!pos->hItem) - return; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy")); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel")); - int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pos->pt.x, pos->pt.y, 0, m_hwnd, nullptr); - if (nReturnCmd == 1) { - CMStringW buf; - GetNodeText(pos->hItem, buf); - Utils_ClipboardCopy(buf); - } - else if (nReturnCmd == 2) { - wchar_t szBuffer[1024]; - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; - tvi.hItem = pos->hItem; - tvi.cchTextMax = _countof(szBuffer); - tvi.pszText = szBuffer; - if (m_tree.GetItem(&tvi)) { - if (wchar_t *str = wcsstr(szBuffer, L": ")) - Utils_ClipboardCopy(str + 2); - else - Utils_ClipboardCopy(szBuffer); - } - } - DestroyMenu(hMenu); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserPhotoDlgProc - Jabber photo dialog - -class JabberUserPhotoDlg : public JabberBaseUserInfoDlg -{ - HBITMAP hBitmap = nullptr; - - CCtrlMButton btnSave; - - UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); - UI_MESSAGE(WM_PAINT, OnPaint); - UI_MESSAGE_MAP_END(); - - char *GetFileName() const - { - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid != nullptr) { - JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); - if (item == nullptr) - item = ppro->ListGetItemPtr(LIST_ROSTER, jid); - if (item != nullptr) - return item->photoFileName; - } - - return nullptr; - } - -public: - JabberUserPhotoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_VCARD_PHOTO), - btnSave(this, IDC_SAVE, g_plugin.getIcon(IDI_SAVE), LPGEN("Save")) - { - btnSave.OnClick = Callback(this, &JabberUserPhotoDlg::onClick_Save); - } - - bool OnInitDialog() override - { - ShowWindow(GetDlgItem(m_hwnd, IDC_LOAD), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_DELETE), SW_HIDE); - return true; - } - - void OnDestroy() override - { - if (hBitmap) { - ppro->debugLogA("Delete bitmap"); - DeleteObject(hBitmap); - } - } - - bool IsEmpty() const override - { - return mir_strlen(GetFileName()) == 0; - } - - bool OnRefresh() override - { - if (hBitmap) { - DeleteObject(hBitmap); - hBitmap = nullptr; - } - btnSave.Hide(); - - char *pszFileName = GetFileName(); - if (mir_strlen(pszFileName)) { - ppro->debugLogA("Showing picture from %s", pszFileName); - hBitmap = Bitmap_Load(Utf2T(pszFileName)); - FreeImage_Premultiply(hBitmap); - btnSave.Show(); - } - - InvalidateRect(m_hwnd, nullptr, TRUE); - UpdateWindow(m_hwnd); - return true; - } - - void onClick_Save(CCtrlButton *) - { - wchar_t szFilter[512]; - - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid == nullptr) - return; - - JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); - if (item == nullptr) - if ((item = ppro->ListGetItemPtr(LIST_ROSTER, jid)) == nullptr) - return; - - switch (ProtoGetAvatarFileFormat(Utf2T(item->photoFileName))) { - case PA_FORMAT_BMP: - mir_snwprintf(szFilter, L"BMP %s (*.bmp)%c*.BMP", TranslateT("format"), 0); - break; - - case PA_FORMAT_GIF: - mir_snwprintf(szFilter, L"GIF %s (*.gif)%c*.GIF", TranslateT("format"), 0); - break; - - case PA_FORMAT_JPEG: - mir_snwprintf(szFilter, L"JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG", TranslateT("format"), 0); - break; - - default: - mir_snwprintf(szFilter, L"%s (*.*)%c*.*", TranslateT("Unknown format"), 0); - } - - wchar_t szFileName[MAX_PATH]; szFileName[0] = '\0'; - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = szFilter; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = _MAX_PATH; - ofn.Flags = OFN_OVERWRITEPROMPT; - if (GetSaveFileName(&ofn)) { - ppro->debugLogW(L"File selected is %s", szFileName); - CopyFile(Utf2T(item->photoFileName), szFileName, FALSE); - } - } - - INT_PTR OnPaint(UINT, WPARAM, LPARAM) - { - if (!ppro->m_bJabberOnline) - SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("")); - else if (!hBitmap) - SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("")); - else { - BITMAP bm; - POINT ptSize, ptOrg, pt, ptFitSize; - RECT rect; - - SetDlgItemTextA(m_hwnd, IDC_CANVAS, ""); - HWND hwndCanvas = GetDlgItem(m_hwnd, IDC_CANVAS); - HDC hdcCanvas = GetDC(hwndCanvas); - HDC hdcMem = CreateCompatibleDC(hdcCanvas); - SelectObject(hdcMem, hBitmap); - SetMapMode(hdcMem, GetMapMode(hdcCanvas)); - GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm); - ptSize.x = bm.bmWidth; - ptSize.y = bm.bmHeight; - DPtoLP(hdcCanvas, &ptSize, 1); - ptOrg.x = ptOrg.y = 0; - DPtoLP(hdcMem, &ptOrg, 1); - GetClientRect(hwndCanvas, &rect); - InvalidateRect(hwndCanvas, nullptr, TRUE); - UpdateWindow(hwndCanvas); - if (ptSize.x <= rect.right && ptSize.y <= rect.bottom) { - pt.x = (rect.right - ptSize.x) / 2; - pt.y = (rect.bottom - ptSize.y) / 2; - ptFitSize = ptSize; - } - else { - if (((float)(ptSize.x - rect.right)) / ptSize.x > ((float)(ptSize.y - rect.bottom)) / ptSize.y) { - ptFitSize.x = rect.right; - ptFitSize.y = (ptSize.y * rect.right) / ptSize.x; - pt.x = 0; - pt.y = (rect.bottom - ptFitSize.y) / 2; - } - else { - ptFitSize.x = (ptSize.x * rect.bottom) / ptSize.y; - ptFitSize.y = rect.bottom; - pt.x = (rect.right - ptFitSize.x) / 2; - pt.y = 0; - } - } - - RECT rc; - if (IsThemeActive()) { - GetClientRect(hwndCanvas, &rc); - DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc); - } - else { - GetClientRect(hwndCanvas, &rc); - FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE)); - } - - if (bm.bmBitsPixel == 32) { - BLENDFUNCTION bf = { 0 }; - bf.AlphaFormat = AC_SRC_ALPHA; - bf.BlendOp = AC_SRC_OVER; - bf.SourceConstantAlpha = 255; - GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf); - } - else { - SetStretchBltMode(hdcCanvas, COLORONCOLOR); - StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY); - } - - DeleteDC(hdcMem); - } - return FALSE; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserPhotoDlgProc - Jabber photo dialog - -static int EnumOwnSessions(const char *szSetting, void *param) -{ - auto *pList = (LIST*)param; - - if (!memcmp(szSetting, omemo::DevicePrefix, sizeof(omemo::DevicePrefix)-1)) - if (szSetting[sizeof(omemo::DevicePrefix)] != 0) - pList->insert(mir_strdup(szSetting)); - - return 0; -} - -static int EnumOmemoSessions(const char *szSetting, void *param) -{ - auto *pList = (LIST*)param; - - if (!memcmp(szSetting, omemo::IdentityPrefix, sizeof(omemo::IdentityPrefix) - 1)) - pList->insert(mir_strdup(szSetting + sizeof(omemo::IdentityPrefix))); - - return 0; -} - -class JabberUserOmemoDlg : public JabberBaseUserInfoDlg -{ - CCtrlListView m_list; - - void AddListItem(const CMStringA &pszStr1, const wchar_t *pszStr2, const CMStringA &pszStr3) - { - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT; - lvi.pszText = mir_a2u(pszStr1); - lvi.iItem = 100500; - int idx = m_list.InsertItem(&lvi); - mir_free(lvi.pszText); - - m_list.SetItemText(idx, 1, (wchar_t *)pszStr2); - CMStringW fp = omemo::FormatFingerprint(pszStr3); - m_list.SetItemText(idx, 2, fp.GetBuffer()); - } - -public: - JabberUserOmemoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_INFO_OMEMO), - m_list(this, IDC_LIST) - { - } - - bool OnInitDialog() override - { - LV_COLUMN lvc = {}; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - - lvc.cx = 90; - lvc.pszText = TranslateT("Device ID"); - m_list.InsertColumn(1, &lvc); - - lvc.cx = 80; - lvc.pszText = TranslateT("Status"); - m_list.InsertColumn(2, &lvc); - - lvc.cx = 550; - lvc.pszText = TranslateT("Fingerprint"); - m_list.InsertColumn(3, &lvc); - - if (m_hContact == 0) - OnRefresh(); - return true; - } - - int Resizer(UTILRESIZECONTROL*) override - { - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - } - - bool OnRefresh() override - { - m_list.DeleteAllItems(); - - if (m_hContact == 0) { - // GetOwnDeviceId() creates own keys if they don't exist - CMStringA str1(FORMAT, "%d", ppro->m_omemo.GetOwnDeviceId()); - CMStringA str2(ppro->getMStringA("OmemoFingerprintOwn")); - AddListItem(str1, TranslateT("Own device"), str2); - } - - for (int i = 0;; i++) { - CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); - uint32_t device_id = ppro->getDword(m_hContact, szSetting, 0); - if (device_id == 0) - break; - - char *jiddev = ppro->getStringA(m_hContact, "jid"); - if (jiddev == 0) - continue; - - size_t len = strlen(jiddev); - jiddev = (char *)mir_realloc(jiddev, len + sizeof(int32_t)); - memcpy(jiddev + len, &device_id, sizeof(int32_t)); - - szSetting = omemo::IdentityPrefix; - szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t)))); - mir_free(jiddev); - - const wchar_t *pwszStatus = L""; - CMStringA fp_hex; - DBVARIANT dbv = { 0 }; - dbv.type = DBVT_BLOB; - db_get(m_hContact, ppro->m_szModuleName, szSetting, &dbv); - if (dbv.cpbVal == 33) { - fp_hex.Truncate(33 * 2); - bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer()); - uint8_t trusted = ppro->getByte(m_hContact, "OmemoFingerprintTrusted_" + fp_hex); - pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED"); - //TODO: 3 states Trusted, Untrusted, TOFU - } - else if (dbv.cpbVal) - pwszStatus = TranslateT("Unknown"); - - db_free(&dbv); - - AddListItem(CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex); - } - return false; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// OnInfoInit - initializes user info option dialogs - -int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact) -{ - if (!Proto_GetAccount(m_szModuleName)) - return 0; - - if (hContact == 0) { - // Show our vcard - OnUserInfoInit_VCard(wParam, hContact); - return 0; - } - - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto != nullptr && !mir_strcmp(szProto, m_szModuleName)) { - USERINFOPAGE uip = {}; - uip.dwInitParam = (LPARAM)this; - uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_ICON; - uip.szGroup.w = m_tszUserName; - uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE); - - uip.pDialog = new JabberUserInfoDlg(this); - uip.position = -2000000000; - uip.szTitle.w = LPGENW("Account"); - g_plugin.addUserInfo(wParam, &uip); - - uip.pDialog = new JabberUserPhotoDlg(this); - uip.position = 2000000000; - uip.szTitle.w = LPGENW("Photo"); - g_plugin.addUserInfo(wParam, &uip); - - CheckOmemoUserInfo(wParam, uip); - } - - return 0; -} - -void CJabberProto::CheckOmemoUserInfo(WPARAM wParam, USERINFOPAGE &uip) -{ - if (m_bUseOMEMO) { - uip.pDialog = new JabberUserOmemoDlg(this); - uip.position = 2000000001; - uip.szTitle.w = L"OMEMO"; - g_plugin.addUserInfo(wParam, &uip); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoUpdate - -void JabberUserInfoInit() -{ - hUserInfoList = WindowList_Create(); -} - -void JabberUserInfoUninit() -{ - WindowList_Destroy(hUserInfoList); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoUpdate - -void JabberUserInfoUpdate(MCONTACT hContact) -{ - if (!hContact) - WindowList_BroadcastAsync(hUserInfoList, WM_PROTO_REFRESH, 0, 0); - else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact)) - PostMessage(hwnd, WM_PROTO_REFRESH, 0, 0); -} +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (c) 2007 Maxim Mluhov +Copyright (C) 2012-23 Miranda NG team + +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. + +*/ + +#include "stdafx.h" + +#include +#include +#include + +#include "jabber_list.h" + +static MWindowList hUserInfoList = nullptr; + +class JabberBaseUserInfoDlg : public CUserInfoPageDlg +{ + INT_PTR DoRefresh(UINT, WPARAM, LPARAM) + { + OnRefresh(); + return 0; + } + +protected: + UI_MESSAGE_MAP(JabberBaseUserInfoDlg, CUserInfoPageDlg); + UI_MESSAGE(WM_PROTO_REFRESH, DoRefresh); + UI_MESSAGE(WM_JABBER_REFRESH_VCARD, DoRefresh); + UI_MESSAGE_MAP_END(); + + CJabberProto *ppro; + + JabberBaseUserInfoDlg(CJabberProto *_ppro, int dlgId) : + CUserInfoPageDlg(g_plugin, dlgId), + ppro(_ppro) + {} +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserInfoDlgProc - main user info dialog + +enum +{ + INFOLINE_DELETE = 0x80000000, + INFOLINE_MASK = 0x7fffffff, + INFOLINE_BAD_ID = 0x7fffffff, + + INFOLINE_NAME = 1, + INFOLINE_MOOD, + INFOLINE_ACTIVITY, + INFOLINE_TUNE, + INFOLINE_OFFLINE, + INFOLINE_MESSAGE, + INFOLINE_SOFTWARE, + INFOLINE_VERSION, + INFOLINE_SYSTEM, + INFOLINE_PRIORITY, + INFOLINE_IDLE, + INFOLINE_CAPS, + INFOLINE_SOFTWARE_INFORMATION, + INFOLINE_SUBSCRIPTION, + INFOLINE_LOGOFF, + INFOLINE_LOGOFF_MSG, + INFOLINE_LASTACTIVE, +}; + +__forceinline uint32_t sttInfoLineId(uint32_t res, uint32_t type, uint32_t line = 0) +{ + return + (type << 24) & 0x7f000000 | + (res << 12) & 0x00fff000 | + (line) & 0x00000fff; +} + +class JabberUserInfoDlg : public JabberBaseUserInfoDlg +{ + JABBER_LIST_ITEM *item = nullptr; + int resourcesCount = -1; + + CCtrlTreeView m_tree; + + UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); + UI_MESSAGE(WM_PROTO_CHECK_ONLINE, OnCheckOnline); + UI_MESSAGE_MAP_END(); + + INT_PTR OnCheckOnline(UINT, WPARAM, LPARAM) + { + if (!ppro->m_bJabberOnline) + item = nullptr; + else + OnRefresh(); + return 0; + } + + //////////////////////////////////////////////////////////////////////////////////////// + // User information block + + void CleanupInfo(int stage) + { + HTREEITEM hItem = m_tree.GetRoot(); + while (hItem) { + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hItem; + m_tree.GetItem(&tvi); + + switch (stage) { + case 0: + tvi.lParam |= INFOLINE_DELETE; + m_tree.SetItem(&tvi); + break; + + case 1: + if (tvi.lParam & INFOLINE_DELETE) { + hItem = m_tree.GetNextSibling(hItem); + m_tree.DeleteItem(tvi.hItem); + continue; + } + break; + } + + HTREEITEM hItemTmp = nullptr; + if (hItemTmp = m_tree.GetChild(hItem)) + hItem = hItemTmp; + else if (hItemTmp = m_tree.GetNextSibling(hItem)) + hItem = hItemTmp; + else { + while (true) { + if (!(hItem = m_tree.GetParent(hItem))) break; + if (hItemTmp = m_tree.GetNextSibling(hItem)) { + hItem = hItemTmp; + break; + } + } + } + } + } + + HTREEITEM FindInfoLine(HTREEITEM htiRoot, LPARAM id = INFOLINE_BAD_ID) + { + if (id == INFOLINE_BAD_ID) return nullptr; + for (HTREEITEM hti = m_tree.GetChild(htiRoot); hti; hti = m_tree.GetNextSibling(hti)) { + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hti; + m_tree.GetItem(&tvi); + if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK)) + return hti; + } + return nullptr; + } + + HTREEITEM FillInfoLine(HTREEITEM htiRoot, HICON hIcon, const wchar_t *title, const char *value, LPARAM id = INFOLINE_BAD_ID, bool expand = false) + { + HTREEITEM hti = FindInfoLine(htiRoot, id); + + Utf2T wszValue(value); + const wchar_t *pwszValue = (value == nullptr) ? TranslateT("") : wszValue; + wchar_t buf[256]; + if (title) + mir_snwprintf(buf, L"%s: %s", title, pwszValue); + else + mir_wstrncpy(buf, pwszValue, _countof(buf)); + + TVINSERTSTRUCT tvis = {}; + tvis.hParent = htiRoot; + tvis.hInsertAfter = TVI_LAST; + tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM; + tvis.itemex.pszText = buf; + tvis.itemex.lParam = id; + + if (hIcon) { + HIMAGELIST himl = m_tree.GetImageList(TVSIL_NORMAL); + tvis.itemex.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvis.itemex.iImage = + tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon); + IcoLib_ReleaseIcon(hIcon); + } + + if (hti) { + tvis.itemex.mask |= TVIF_HANDLE; + tvis.itemex.hItem = hti; + m_tree.SetItem(&tvis.itemex); + } + else { + tvis.itemex.mask |= TVIF_STATE; + tvis.itemex.stateMask = TVIS_EXPANDED; + tvis.itemex.state = expand ? TVIS_EXPANDED : 0; + hti = m_tree.InsertItem(&tvis); + } + + return hti; + } + + void FillAdvStatusInfo(HTREEITEM htiRoot, uint32_t dwInfoLine, MCONTACT hContact, wchar_t *szTitle, char *pszSlot) + { + ptrA szAdvStatusIcon(ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON)); + ptrW szAdvStatusTitle(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE)); + ptrW szAdvStatusText(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT)); + + if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) { + wchar_t szText[2048]; + if (szAdvStatusText && *szAdvStatusText) + mir_snwprintf(szText, L"%s (%s)", TranslateW(szAdvStatusTitle), szAdvStatusText.get()); + else + wcsncpy_s(szText, TranslateW(szAdvStatusTitle), _TRUNCATE); + FillInfoLine(htiRoot, IcoLib_GetIcon(szAdvStatusIcon), szTitle, T2Utf(szText), dwInfoLine); + } + } + + void FillResourceInfo(HTREEITEM htiRoot, int resource) + { + HTREEITEM htiResource = htiRoot; + pResourceStatus r = resource ? item->arResources[resource - 1] : item->getTemp(); + + if (r->m_szResourceName && *r->m_szResourceName) + htiResource = FillInfoLine(htiRoot, Skin_LoadProtoIcon(ppro->m_szModuleName, r->m_iStatus), + TranslateT("Resource"), r->m_szResourceName, sttInfoLineId(resource, INFOLINE_NAME), true); + + // StatusMsg + FillInfoLine(htiResource, nullptr /*Skin_LoadIcon(SKINICON_EVENT_MESSAGE)*/, + TranslateT("Message"), r->m_szStatusMessage, + sttInfoLineId(resource, INFOLINE_MESSAGE)); + + // Software + if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { + HICON hIcon = nullptr; + + if (ServiceExists(MS_FP_GETCLIENTICONT)) { + if (pCaps->GetSoft()) { + wchar_t buf[256]; + mir_snwprintf(buf, L"%s %s", pCaps->GetSoft(), pCaps->GetSoftVer()); + hIcon = Finger_GetClientIcon(buf, 0); + } + } + + FillInfoLine(htiResource, hIcon, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE)); + + // Version + FillInfoLine(htiResource, nullptr, TranslateT("Version"), pCaps->GetSoftMir() ? pCaps->GetSoftMir() : pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_VERSION)); + + // System + FillInfoLine(htiResource, nullptr, TranslateT("System"), pCaps->GetOsVer() ? pCaps->GetOsVer() : pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SYSTEM)); + + if (hIcon) + DestroyIcon(hIcon); + } + + // Resource priority + char buf[256]; + itoa(r->m_iPriority, buf, 10); + FillInfoLine(htiResource, nullptr, TranslateT("Resource priority"), buf, sttInfoLineId(resource, INFOLINE_PRIORITY)); + + // Idle + if (r->m_dwIdleStartTime != -1) { + if (r->m_dwIdleStartTime != 0) { + mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); + size_t len = mir_strlen(buf); + if (len > 0) + buf[len - 1] = 0; + } + else mir_strncpy(buf, TranslateU(""), _countof(buf)); + + FillInfoLine(htiResource, nullptr, TranslateT("Last activity"), buf, sttInfoLineId(resource, INFOLINE_IDLE)); + } + + // caps + JabberCapsBits jcb = ppro->GetResourceCapabilities(MakeJid(item->jid, r->m_szResourceName), r); + if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) { + HTREEITEM htiCaps = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS)); + int i; + for (i = 0; i < g_cJabberFeatCapPairs; i++) + if (jcb & g_JabberFeatCapPairs[i].jcbCap) { + char szDescription[1024]; + if (g_JabberFeatCapPairs[i].tszDescription) + mir_snprintf(szDescription, "%s (%s)", TranslateU(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature); + else + strncpy_s(szDescription, g_JabberFeatCapPairs[i].szFeature, _TRUNCATE); + FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i)); + } + + for (auto &it : ppro->m_lstJabberFeatCapPairsDynamic) { + if (jcb & it->jcbCap) { + char szDescription[1024]; + if (it->szDescription) + mir_snprintf(szDescription, "%s (%s)", TranslateU(it->szDescription), it->szFeature); + else + strncpy_s(szDescription, it->szFeature, _TRUNCATE); + FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i++)); + } + } + } + + // Software info + HTREEITEM htiSoftwareInfo = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION)); + int nLineId = 0; + if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { + if (pCaps->GetOs()) + FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system"), pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); + if (pCaps->GetOsVer()) + FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system version"), pCaps->GetOsVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); + if (pCaps->GetSoft()) + FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); + if (pCaps->GetSoftVer()) + FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software version"), pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); + if (pCaps->GetSoftMir()) + FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Miranda core version"), pCaps->GetSoftMir(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); + } + } + + void FillUserInfo() + { + m_tree.SendMsg(WM_SETREDRAW, FALSE, 0); + + CleanupInfo(0); + + HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", item->jid, sttInfoLineId(0, INFOLINE_NAME), true); + + if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) { + FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD); + FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY); + FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE); + } + + // subscription + switch (item->subscription) { + case SUB_BOTH: + FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); + break; + case SUB_TO: + FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); + break; + case SUB_FROM: + FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); + break; + default: + FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); + break; + } + + // logoff + char buf[256]; + JABBER_RESOURCE_STATUS *r = item->getTemp(); + if (r->m_dwIdleStartTime != -1) { + if (r->m_dwIdleStartTime > 0) { + mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); + size_t len = mir_strlen(buf); + if (len > 0) + buf[len - 1] = 0; + } + else mir_strncpy(buf, TranslateU(""), _countof(buf)); + + FillInfoLine(htiRoot, nullptr, + (item->jid && strchr(item->jid, '@')) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf, + sttInfoLineId(0, INFOLINE_LOGOFF)); + } + + if (r->m_szStatusMessage) + FillInfoLine(htiRoot, nullptr, TranslateT("Logoff message"), r->m_szStatusMessage, sttInfoLineId(0, INFOLINE_LOGOFF_MSG)); + + // activity + if (item->m_pLastSeenResource) + mir_strncpy(buf, item->m_pLastSeenResource->m_szResourceName, _countof(buf)); + else + mir_strncpy(buf, TranslateU(""), _countof(buf)); + FillInfoLine(htiRoot, nullptr, TranslateT("Last active resource"), buf, sttInfoLineId(0, INFOLINE_LASTACTIVE)); + + // resources + if (item->arResources.getCount()) { + for (int i = 0; i < item->arResources.getCount(); i++) + FillResourceInfo(htiRoot, i + 1); + } + else if (!strchr(item->jid, '@') || (r->m_iStatus != ID_STATUS_OFFLINE)) + FillResourceInfo(htiRoot, 0); + + CleanupInfo(1); + m_tree.SendMsg(WM_SETREDRAW, TRUE, 0); + + RedrawWindow(m_tree.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE); + } + +public: + JabberUserInfoDlg(CJabberProto *_ppro) : + JabberBaseUserInfoDlg(_ppro, IDD_INFO_JABBER), + m_tree(this, IDC_TV_INFO) + { + m_tree.OnBuildMenu = Callback(this, &JabberUserInfoDlg::onMenu_Tree); + } + + bool OnInitDialog() override + { + ppro->WindowSubscribe(m_hwnd); + Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_USERDETAILS); + + RECT rc; + GetClientRect(m_hwnd, &rc); + MoveWindow(m_tree.GetHwnd(), 5, 5, rc.right - 10, rc.bottom - 10, TRUE); + + HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); + ImageList_AddSkinIcon(himl, SKINICON_OTHER_SMALLDOT); + TreeView_SetImageList(m_tree.GetHwnd(), himl, TVSIL_NORMAL); + + WindowList_Add(hUserInfoList, m_hwnd, m_hContact); + return true; + } + + void OnDestroy() override + { + ppro->WindowUnsubscribe(m_hwnd); + WindowList_Remove(hUserInfoList, m_hwnd); + ImageList_Destroy(m_tree.SetImageList(nullptr, TVSIL_NORMAL)); + Window_FreeIcon_IcoLib(m_hwnd); + } + + int Resizer(UTILRESIZECONTROL*) override + { + return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; + } + + bool OnRefresh() override + { + if (item == nullptr) { + ptrA jid(ppro->getUStringA(m_hContact, "jid")); + if (jid == nullptr) + return false; + + if (ppro->m_bJabberOnline) + if (!(item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid))) + item = ppro->ListGetItemPtr(LIST_ROSTER, jid); + + if (item == nullptr) { + m_tree.DeleteAllItems(); + HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", jid, sttInfoLineId(0, INFOLINE_NAME), true); + FillInfoLine(htiRoot, g_plugin.getIcon(IDI_VCARD), nullptr, TranslateU("Please switch online to see more details.")); + return false; + } + } + FillUserInfo(); + return false; + } + + //////////////////////////////////////////////////////////////////////////////////////// + // Context menu + + void GetNodeText(HTREEITEM hti, CMStringW &buf, int indent = 0) + { + for (int i = 0; i < indent; i++) + buf.AppendChar('\t'); + + wchar_t wszText[256]; + TVITEMEX tvi = {}; + tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; + tvi.hItem = hti; + tvi.cchTextMax = _countof(wszText); + tvi.pszText = wszText; + if (!m_tree.GetItem(&tvi)) // failure, maybe item was removed... + return; + + buf.Append(wszText); + buf.Append(L"\r\n"); + + if (tvi.state & TVIS_EXPANDED) + for (hti = m_tree.GetChild(hti); hti; hti = m_tree.GetNextSibling(hti)) + GetNodeText(hti, buf, indent + 1); + } + + void onMenu_Tree(CContextMenuPos *pos) + { + if (!pos->hItem) + return; + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy")); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value")); + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel")); + int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pos->pt.x, pos->pt.y, 0, m_hwnd, nullptr); + if (nReturnCmd == 1) { + CMStringW buf; + GetNodeText(pos->hItem, buf); + Utils_ClipboardCopy(buf); + } + else if (nReturnCmd == 2) { + wchar_t szBuffer[1024]; + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; + tvi.hItem = pos->hItem; + tvi.cchTextMax = _countof(szBuffer); + tvi.pszText = szBuffer; + if (m_tree.GetItem(&tvi)) { + if (wchar_t *str = wcsstr(szBuffer, L": ")) + Utils_ClipboardCopy(str + 2); + else + Utils_ClipboardCopy(szBuffer); + } + } + DestroyMenu(hMenu); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserPhotoDlgProc - Jabber photo dialog + +class JabberUserPhotoDlg : public JabberBaseUserInfoDlg +{ + HBITMAP hBitmap = nullptr; + + CCtrlMButton btnSave; + + UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); + UI_MESSAGE(WM_PAINT, OnPaint); + UI_MESSAGE_MAP_END(); + + char *GetFileName() const + { + ptrA jid(ppro->getUStringA(m_hContact, "jid")); + if (jid != nullptr) { + JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); + if (item == nullptr) + item = ppro->ListGetItemPtr(LIST_ROSTER, jid); + if (item != nullptr) + return item->photoFileName; + } + + return nullptr; + } + +public: + JabberUserPhotoDlg(CJabberProto *_ppro) : + JabberBaseUserInfoDlg(_ppro, IDD_VCARD_PHOTO), + btnSave(this, IDC_SAVE, g_plugin.getIcon(IDI_SAVE), LPGEN("Save")) + { + btnSave.OnClick = Callback(this, &JabberUserPhotoDlg::onClick_Save); + } + + bool OnInitDialog() override + { + ShowWindow(GetDlgItem(m_hwnd, IDC_LOAD), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_DELETE), SW_HIDE); + return true; + } + + void OnDestroy() override + { + if (hBitmap) { + ppro->debugLogA("Delete bitmap"); + DeleteObject(hBitmap); + } + } + + bool IsEmpty() const override + { + return mir_strlen(GetFileName()) == 0; + } + + bool OnRefresh() override + { + if (hBitmap) { + DeleteObject(hBitmap); + hBitmap = nullptr; + } + btnSave.Hide(); + + char *pszFileName = GetFileName(); + if (mir_strlen(pszFileName)) { + ppro->debugLogA("Showing picture from %s", pszFileName); + hBitmap = Bitmap_Load(Utf2T(pszFileName)); + FreeImage_Premultiply(hBitmap); + btnSave.Show(); + } + + InvalidateRect(m_hwnd, nullptr, TRUE); + UpdateWindow(m_hwnd); + return true; + } + + void onClick_Save(CCtrlButton *) + { + wchar_t szFilter[512]; + + ptrA jid(ppro->getUStringA(m_hContact, "jid")); + if (jid == nullptr) + return; + + JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); + if (item == nullptr) + if ((item = ppro->ListGetItemPtr(LIST_ROSTER, jid)) == nullptr) + return; + + switch (ProtoGetAvatarFileFormat(Utf2T(item->photoFileName))) { + case PA_FORMAT_BMP: + mir_snwprintf(szFilter, L"BMP %s (*.bmp)%c*.BMP", TranslateT("format"), 0); + break; + + case PA_FORMAT_GIF: + mir_snwprintf(szFilter, L"GIF %s (*.gif)%c*.GIF", TranslateT("format"), 0); + break; + + case PA_FORMAT_JPEG: + mir_snwprintf(szFilter, L"JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG", TranslateT("format"), 0); + break; + + default: + mir_snwprintf(szFilter, L"%s (*.*)%c*.*", TranslateT("Unknown format"), 0); + } + + wchar_t szFileName[MAX_PATH]; szFileName[0] = '\0'; + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = szFilter; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = _MAX_PATH; + ofn.Flags = OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn)) { + ppro->debugLogW(L"File selected is %s", szFileName); + CopyFile(Utf2T(item->photoFileName), szFileName, FALSE); + } + } + + INT_PTR OnPaint(UINT, WPARAM, LPARAM) + { + if (!ppro->m_bJabberOnline) + SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("")); + else if (!hBitmap) + SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("")); + else { + BITMAP bm; + POINT ptSize, ptOrg, pt, ptFitSize; + RECT rect; + + SetDlgItemTextA(m_hwnd, IDC_CANVAS, ""); + HWND hwndCanvas = GetDlgItem(m_hwnd, IDC_CANVAS); + HDC hdcCanvas = GetDC(hwndCanvas); + HDC hdcMem = CreateCompatibleDC(hdcCanvas); + SelectObject(hdcMem, hBitmap); + SetMapMode(hdcMem, GetMapMode(hdcCanvas)); + GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm); + ptSize.x = bm.bmWidth; + ptSize.y = bm.bmHeight; + DPtoLP(hdcCanvas, &ptSize, 1); + ptOrg.x = ptOrg.y = 0; + DPtoLP(hdcMem, &ptOrg, 1); + GetClientRect(hwndCanvas, &rect); + InvalidateRect(hwndCanvas, nullptr, TRUE); + UpdateWindow(hwndCanvas); + if (ptSize.x <= rect.right && ptSize.y <= rect.bottom) { + pt.x = (rect.right - ptSize.x) / 2; + pt.y = (rect.bottom - ptSize.y) / 2; + ptFitSize = ptSize; + } + else { + if (((float)(ptSize.x - rect.right)) / ptSize.x > ((float)(ptSize.y - rect.bottom)) / ptSize.y) { + ptFitSize.x = rect.right; + ptFitSize.y = (ptSize.y * rect.right) / ptSize.x; + pt.x = 0; + pt.y = (rect.bottom - ptFitSize.y) / 2; + } + else { + ptFitSize.x = (ptSize.x * rect.bottom) / ptSize.y; + ptFitSize.y = rect.bottom; + pt.x = (rect.right - ptFitSize.x) / 2; + pt.y = 0; + } + } + + RECT rc; + if (IsThemeActive()) { + GetClientRect(hwndCanvas, &rc); + DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc); + } + else { + GetClientRect(hwndCanvas, &rc); + FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE)); + } + + if (bm.bmBitsPixel == 32) { + BLENDFUNCTION bf = { 0 }; + bf.AlphaFormat = AC_SRC_ALPHA; + bf.BlendOp = AC_SRC_OVER; + bf.SourceConstantAlpha = 255; + GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf); + } + else { + SetStretchBltMode(hdcCanvas, COLORONCOLOR); + StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY); + } + + DeleteDC(hdcMem); + } + return FALSE; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserPhotoDlgProc - Jabber photo dialog + +static int EnumOwnSessions(const char *szSetting, void *param) +{ + auto *pList = (LIST*)param; + + if (!memcmp(szSetting, omemo::DevicePrefix, sizeof(omemo::DevicePrefix)-1)) + if (szSetting[sizeof(omemo::DevicePrefix)] != 0) + pList->insert(mir_strdup(szSetting)); + + return 0; +} + +static int EnumOmemoSessions(const char *szSetting, void *param) +{ + auto *pList = (LIST*)param; + + if (!memcmp(szSetting, omemo::IdentityPrefix, sizeof(omemo::IdentityPrefix) - 1)) + pList->insert(mir_strdup(szSetting + sizeof(omemo::IdentityPrefix))); + + return 0; +} + +class JabberUserOmemoDlg : public JabberBaseUserInfoDlg +{ + CCtrlListView m_list; + + void AddListItem(const CMStringA &pszStr1, const wchar_t *pszStr2, const CMStringA &pszStr3) + { + LVITEM lvi = {}; + lvi.mask = LVIF_TEXT; + lvi.pszText = mir_a2u(pszStr1); + lvi.iItem = 100500; + int idx = m_list.InsertItem(&lvi); + mir_free(lvi.pszText); + + m_list.SetItemText(idx, 1, (wchar_t *)pszStr2); + CMStringW fp = omemo::FormatFingerprint(pszStr3); + m_list.SetItemText(idx, 2, fp.GetBuffer()); + } + +public: + JabberUserOmemoDlg(CJabberProto *_ppro) : + JabberBaseUserInfoDlg(_ppro, IDD_INFO_OMEMO), + m_list(this, IDC_LIST) + { + } + + bool OnInitDialog() override + { + LV_COLUMN lvc = {}; + lvc.mask = LVCF_TEXT | LVCF_WIDTH; + + lvc.cx = 90; + lvc.pszText = TranslateT("Device ID"); + m_list.InsertColumn(1, &lvc); + + lvc.cx = 80; + lvc.pszText = TranslateT("Status"); + m_list.InsertColumn(2, &lvc); + + lvc.cx = 550; + lvc.pszText = TranslateT("Fingerprint"); + m_list.InsertColumn(3, &lvc); + + if (m_hContact == 0) + OnRefresh(); + return true; + } + + int Resizer(UTILRESIZECONTROL*) override + { + return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; + } + + bool OnRefresh() override + { + m_list.DeleteAllItems(); + + if (m_hContact == 0) { + // GetOwnDeviceId() creates own keys if they don't exist + CMStringA str1(FORMAT, "%d", ppro->m_omemo.GetOwnDeviceId()); + CMStringA str2(ppro->getMStringA("OmemoFingerprintOwn")); + AddListItem(str1, TranslateT("Own device"), str2); + } + + for (int i = 0;; i++) { + CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); + uint32_t device_id = ppro->getDword(m_hContact, szSetting, 0); + if (device_id == 0) + break; + + char *jiddev = ppro->getStringA(m_hContact, "jid"); + if (jiddev == 0) + continue; + + size_t len = strlen(jiddev); + jiddev = (char *)mir_realloc(jiddev, len + sizeof(int32_t)); + memcpy(jiddev + len, &device_id, sizeof(int32_t)); + + szSetting = omemo::IdentityPrefix; + szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t)))); + mir_free(jiddev); + + const wchar_t *pwszStatus = L""; + CMStringA fp_hex; + DBVARIANT dbv = { 0 }; + dbv.type = DBVT_BLOB; + db_get(m_hContact, ppro->m_szModuleName, szSetting, &dbv); + if (dbv.cpbVal == 33) { + fp_hex.Truncate(33 * 2); + bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer()); + uint8_t trusted = ppro->getByte(m_hContact, "OmemoFingerprintTrusted_" + fp_hex); + pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED"); + //TODO: 3 states Trusted, Untrusted, TOFU + } + else if (dbv.cpbVal) + pwszStatus = TranslateT("Unknown"); + + db_free(&dbv); + + AddListItem(CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex); + } + return false; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// OnInfoInit - initializes user info option dialogs + +int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact) +{ + if (!Proto_GetAccount(m_szModuleName)) + return 0; + + if (hContact == 0) { + // Show our vcard + OnUserInfoInit_VCard(wParam, hContact); + return 0; + } + + char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto != nullptr && !mir_strcmp(szProto, m_szModuleName)) { + USERINFOPAGE uip = {}; + uip.dwInitParam = (LPARAM)this; + uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_ICON; + uip.szGroup.w = m_tszUserName; + uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE); + + uip.pDialog = new JabberUserInfoDlg(this); + uip.position = -2000000000; + uip.szTitle.w = LPGENW("Account"); + g_plugin.addUserInfo(wParam, &uip); + + uip.pDialog = new JabberUserPhotoDlg(this); + uip.position = 2000000000; + uip.szTitle.w = LPGENW("Photo"); + g_plugin.addUserInfo(wParam, &uip); + + CheckOmemoUserInfo(wParam, uip); + } + + return 0; +} + +void CJabberProto::CheckOmemoUserInfo(WPARAM wParam, USERINFOPAGE &uip) +{ + if (m_bUseOMEMO) { + uip.pDialog = new JabberUserOmemoDlg(this); + uip.position = 2000000001; + uip.szTitle.w = L"OMEMO"; + g_plugin.addUserInfo(wParam, &uip); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserInfoUpdate + +void JabberUserInfoInit() +{ + hUserInfoList = WindowList_Create(); +} + +void JabberUserInfoUninit() +{ + WindowList_Destroy(hUserInfoList); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberUserInfoUpdate + +void JabberUserInfoUpdate(MCONTACT hContact) +{ + if (!hContact) + WindowList_BroadcastAsync(hUserInfoList, WM_PROTO_REFRESH, 0, 0); + else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact)) + PostMessage(hwnd, WM_PROTO_REFRESH, 0, 0); +} diff --git a/protocols/JabberG/src/jabber_util.cpp b/protocols/JabberG/src/jabber_util.cpp index 3eebd4e2c7..a3e1c856f6 100644 --- a/protocols/JabberG/src/jabber_util.cpp +++ b/protocols/JabberG/src/jabber_util.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_vcard.cpp b/protocols/JabberG/src/jabber_vcard.cpp index 0d722aac37..aad0f8d9be 100644 --- a/protocols/JabberG/src/jabber_vcard.cpp +++ b/protocols/JabberG/src/jabber_vcard.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_xml.cpp b/protocols/JabberG/src/jabber_xml.cpp index b53ca07d45..c3a166017a 100644 --- a/protocols/JabberG/src/jabber_xml.cpp +++ b/protocols/JabberG/src/jabber_xml.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_xml.h b/protocols/JabberG/src/jabber_xml.h index 16f2626b1c..4ecdde53ea 100644 --- a/protocols/JabberG/src/jabber_xml.h +++ b/protocols/JabberG/src/jabber_xml.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_xstatus.cpp b/protocols/JabberG/src/jabber_xstatus.cpp index b67347f7e7..0a989d9dd6 100644 --- a/protocols/JabberG/src/jabber_xstatus.cpp +++ b/protocols/JabberG/src/jabber_xstatus.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_xstatus.h b/protocols/JabberG/src/jabber_xstatus.h index 0d2bbb266a..80358daca6 100644 --- a/protocols/JabberG/src/jabber_xstatus.h +++ b/protocols/JabberG/src/jabber_xstatus.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/jabber_zstream.cpp b/protocols/JabberG/src/jabber_zstream.cpp index 0931e4fc34..87f21d44aa 100644 --- a/protocols/JabberG/src/jabber_zstream.cpp +++ b/protocols/JabberG/src/jabber_zstream.cpp @@ -6,7 +6,7 @@ XEP-0138 (Stream Compression) implementation Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Kostya Chukavin, Taras Zackrepa -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/stdafx.cxx b/protocols/JabberG/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/JabberG/src/stdafx.cxx +++ b/protocols/JabberG/src/stdafx.cxx @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/stdafx.h b/protocols/JabberG/src/stdafx.h index da81c661c9..d848e5a215 100644 --- a/protocols/JabberG/src/stdafx.h +++ b/protocols/JabberG/src/stdafx.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/JabberG/src/version.h b/protocols/JabberG/src/version.h index 29a9e971eb..1308d51095 100644 --- a/protocols/JabberG/src/version.h +++ b/protocols/JabberG/src/version.h @@ -9,5 +9,5 @@ #define __FILENAME "Jabber.dll" #define __DESCRIPTION "Jabber (XMPP) protocol support for Miranda NG." #define __AUTHOR "George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura" -#define __COPYRIGHT "© 2005-22 George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura" +#define __COPYRIGHT "© 2005-23 George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura" #define __AUTHORWEB "https://miranda-ng.org/p/Jabber" -- cgit v1.2.3