summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin15/mir_icons.sln10
-rw-r--r--bin16/mir_icons.sln10
-rw-r--r--bin17/mir_icons.sln10
-rw-r--r--build/build.no2
-rw-r--r--build/build.no.stable2
-rw-r--r--include/m_netlib.h2
-rw-r--r--langpacks/english/=CORE=.txt2
-rw-r--r--langpacks/english/Plugins/Teams.txt81
-rw-r--r--langpacks/english/Plugins/Weather.txt22
-rw-r--r--langpacks/russian/=CORE=.txt2
-rw-r--r--langpacks/russian/Langpack_russian.txt167
-rw-r--r--langpacks/russian/Plugins/Folders.txt2
-rw-r--r--langpacks/russian/Plugins/Teams.txt143
-rw-r--r--langpacks/russian/Plugins/Weather.txt44
-rw-r--r--langpacks/russian/Untranslated/Teams.txt14
-rw-r--r--langpacks/russian/Weather/Weather_forecast.txt2
-rw-r--r--libs/win32/mir_app.libbin303972 -> 304322 bytes
-rw-r--r--libs/win64/mir_app.libbin304078 -> 304444 bytes
-rw-r--r--plugins/TabSRMM/src/msgdlgother.cpp2
-rw-r--r--protocols/CloudFile/res/dropbox.icobin5430 -> 5430 bytes
-rw-r--r--protocols/CloudFile/res/gdrive.icobin5430 -> 5430 bytes
-rw-r--r--protocols/CloudFile/res/onedrive.icobin7886 -> 5430 bytes
-rw-r--r--protocols/CloudFile/res/upload.icobin103999 -> 5430 bytes
-rw-r--r--protocols/CloudFile/res/yadisk.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/proto_discord/res/Away.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/proto_discord/res/DND.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/proto_discord/res/Invisible.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/proto_discord/res/Offline.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/proto_discord/res/Online.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Discord/res/discord.icobin4150 -> 5430 bytes
-rw-r--r--protocols/Discord/src/gateway.cpp4
-rw-r--r--protocols/Discord/src/voice_client.cpp2
-rw-r--r--protocols/ICQCorp/CMakeLists.txt3
-rw-r--r--protocols/ICQCorp/Proto_icq/CMakeLists.txt2
-rw-r--r--protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj28
-rw-r--r--protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj.filters4
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Away.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/DND.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/FFC.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Invisible.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/NA.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Occupied.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Offline.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Online.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Phone.icobin0 -> 5430 bytes
-rw-r--r--protocols/ICQCorp/Proto_icq/res/Proto_ICQ.rc77
-rw-r--r--protocols/ICQCorp/Proto_icq/src/resource.h24
-rw-r--r--protocols/SkypeWeb/SkypeWeb.vcxproj1
-rw-r--r--protocols/SkypeWeb/SkypeWeb.vcxproj.filters3
-rw-r--r--protocols/SkypeWeb/src/requests/poll.h38
-rw-r--r--protocols/SkypeWeb/src/skype_polling.cpp30
-rw-r--r--protocols/SkypeWeb/src/stdafx.h1
-rw-r--r--protocols/Steam/proto_steam/res/Invisible.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Steam/src/steam_login.cpp4
-rw-r--r--protocols/Teams/Teams.vcxproj2
-rw-r--r--protocols/Teams/Teams.vcxproj.filters6
-rw-r--r--protocols/Teams/proto_teams/res/Away.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Teams/proto_teams/res/DND.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Teams/proto_teams/res/Invisible.icobin5430 -> 5430 bytes
-rw-r--r--protocols/Teams/src/main.cpp17
-rw-r--r--protocols/Teams/src/requests/capabilities.h41
-rw-r--r--protocols/Teams/src/requests/contacts.h89
-rw-r--r--protocols/Teams/src/requests/poll.h38
-rw-r--r--protocols/Teams/src/stdafx.cxx2
-rw-r--r--protocols/Teams/src/stdafx.h7
-rw-r--r--protocols/Teams/src/teams_avatars.cpp4
-rw-r--r--protocols/Teams/src/teams_chatrooms.cpp6
-rw-r--r--protocols/Teams/src/teams_contacts.cpp53
-rw-r--r--protocols/Teams/src/teams_endpoint.cpp71
-rw-r--r--protocols/Teams/src/teams_files.cpp19
-rw-r--r--protocols/Teams/src/teams_history.cpp6
-rw-r--r--protocols/Teams/src/teams_http.cpp4
-rw-r--r--protocols/Teams/src/teams_login.cpp4
-rw-r--r--protocols/Teams/src/teams_menus.cpp2
-rw-r--r--protocols/Teams/src/teams_mood.cpp2
-rw-r--r--protocols/Teams/src/teams_options.cpp2
-rw-r--r--protocols/Teams/src/teams_polling.cpp39
-rw-r--r--protocols/Teams/src/teams_popups.cpp17
-rw-r--r--protocols/Teams/src/teams_profile.cpp4
-rw-r--r--protocols/Teams/src/teams_proto.cpp40
-rw-r--r--protocols/Teams/src/teams_proto.h70
-rw-r--r--protocols/Teams/src/teams_search.cpp4
-rw-r--r--protocols/Teams/src/teams_trouter.cpp367
-rw-r--r--protocols/Teams/src/teams_utils.cpp2
-rw-r--r--protocols/Teams/src/teams_utils.h4
-rw-r--r--protocols/Teams/src/version.h2
-rw-r--r--protocols/Weather/res/resource.rc3
-rw-r--r--protocols/Weather/src/proto.h2
-rw-r--r--protocols/Weather/src/resource.h1
-rw-r--r--protocols/Weather/src/stdafx.h2
-rw-r--r--protocols/Weather/src/weather_addstn.cpp2
-rw-r--r--protocols/Weather/src/weather_contacts.cpp18
-rw-r--r--protocols/Weather/src/weather_conv.cpp59
-rw-r--r--protocols/Weather/src/weather_data.cpp16
-rw-r--r--protocols/Weather/src/weather_mwin.cpp4
-rw-r--r--protocols/Weather/src/weather_opt.cpp98
-rw-r--r--protocols/Weather/src/weather_popup.cpp4
-rw-r--r--protocols/Weather/src/weather_proto.cpp4
-rw-r--r--protocols/Weather/src/weather_update.cpp60
-rw-r--r--protocols/Weather/src/weather_userinfo.cpp20
-rw-r--r--src/mir_app/src/MHttpRequest.cpp9
-rw-r--r--src/mir_app/src/mir_app.def1
-rw-r--r--src/mir_app/src/mir_app64.def1
-rw-r--r--src/mir_app/src/netlib_websocket.cpp2
-rw-r--r--src/mir_app/src/proto_order.cpp36
-rw-r--r--src/mir_app/src/srmm_base.cpp4
-rw-r--r--tools/build_scripts/z2_PackPluginUpdater.bat1
107 files changed, 1433 insertions, 576 deletions
diff --git a/bin15/mir_icons.sln b/bin15/mir_icons.sln
index 78cbb7e6fd..3bdaf6a035 100644
--- a/bin15/mir_icons.sln
+++ b/bin15/mir_icons.sln
@@ -62,6 +62,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TabSRMM_icons", "..\plugins
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_Teams", "..\protocols\Teams\proto_teams\Proto_Teams.vcxproj", "{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_ICQ", "..\protocols\ICQCorp\Proto_icq\Proto_ICQ.vcxproj", "{DB3B0449-E576-4BBB-8B08-AB9E914D39CA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -310,6 +312,14 @@ Global
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|Win32.Build.0 = Release|Win32
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.ActiveCfg = Release|x64
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.Build.0 = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.Build.0 = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.ActiveCfg = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.Build.0 = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.ActiveCfg = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.Build.0 = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.ActiveCfg = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/bin16/mir_icons.sln b/bin16/mir_icons.sln
index f5b98324bd..df811e7aa7 100644
--- a/bin16/mir_icons.sln
+++ b/bin16/mir_icons.sln
@@ -62,6 +62,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TabSRMM_icons", "..\plugins
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_Teams", "..\protocols\Teams\proto_teams\Proto_Teams.vcxproj", "{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_ICQ", "..\protocols\ICQCorp\Proto_icq\Proto_ICQ.vcxproj", "{DB3B0449-E576-4BBB-8B08-AB9E914D39CA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -310,6 +312,14 @@ Global
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|Win32.Build.0 = Release|Win32
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.ActiveCfg = Release|x64
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.Build.0 = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.Build.0 = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.ActiveCfg = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.Build.0 = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.ActiveCfg = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.Build.0 = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.ActiveCfg = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/bin17/mir_icons.sln b/bin17/mir_icons.sln
index 0e4a0f0422..b36b8b2fce 100644
--- a/bin17/mir_icons.sln
+++ b/bin17/mir_icons.sln
@@ -62,6 +62,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TabSRMM_icons", "..\plugins
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_Teams", "..\protocols\Teams\proto_teams\Proto_Teams.vcxproj", "{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proto_ICQ", "..\protocols\ICQCorp\Proto_icq\Proto_ICQ.vcxproj", "{DB3B0449-E576-4BBB-8B08-AB9E914D39CA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -310,6 +312,14 @@ Global
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|Win32.Build.0 = Release|Win32
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.ActiveCfg = Release|x64
{9C0BBF52-FE1D-4F07-9422-2B3321CFBE88}.Release|x64.Build.0 = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|Win32.Build.0 = Debug|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.ActiveCfg = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Debug|x64.Build.0 = Debug|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.ActiveCfg = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|Win32.Build.0 = Release|Win32
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.ActiveCfg = Release|x64
+ {DB3B0449-E576-4BBB-8B08-AB9E914D39CA}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build/build.no b/build/build.no
index fdf58ee5b7..a2d132a29d 100644
--- a/build/build.no
+++ b/build/build.no
@@ -1 +1 @@
-0 96 6 \ No newline at end of file
+0 96 7 \ No newline at end of file
diff --git a/build/build.no.stable b/build/build.no.stable
index 1542dd848d..fdf58ee5b7 100644
--- a/build/build.no.stable
+++ b/build/build.no.stable
@@ -1 +1 @@
-0 96 5 \ No newline at end of file
+0 96 6 \ No newline at end of file
diff --git a/include/m_netlib.h b/include/m_netlib.h
index 3ff4f971d8..3121ce633e 100644
--- a/include/m_netlib.h
+++ b/include/m_netlib.h
@@ -540,11 +540,13 @@ public:
};
MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT_PARAM&);
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const BOOL_PARAM&);
MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const CHAR_PARAM&);
MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT64_PARAM&);
MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const WCHAR_PARAM&);
__forceinline MHttpRequest* operator<<(MHttpRequest &req, const INT_PARAM &param) { return &req << param; }
+__forceinline MHttpRequest* operator<<(MHttpRequest &req, const BOOL_PARAM &param) { return &req << param; }
__forceinline MHttpRequest* operator<<(MHttpRequest &req, const CHAR_PARAM &param) { return &req << param; }
__forceinline MHttpRequest* operator<<(MHttpRequest &req, const INT64_PARAM &param) { return &req << param; }
__forceinline MHttpRequest* operator<<(MHttpRequest &req, const WCHAR_PARAM &param) { return &req << param; }
diff --git a/langpacks/english/=CORE=.txt b/langpacks/english/=CORE=.txt
index 04ff7f3638..edadf4c10b 100644
--- a/langpacks/english/=CORE=.txt
+++ b/langpacks/english/=CORE=.txt
@@ -1,7 +1,7 @@
;============================================================
; File: miranda32/64.exe
; Module: Miranda Core
-; Version: 0.96.6
+; Version: 0.96.7
;============================================================
;file \src\core\stdautoaway\res\resource.rc
[Become idle if the following is left unattended:]
diff --git a/langpacks/english/Plugins/Teams.txt b/langpacks/english/Plugins/Teams.txt
new file mode 100644
index 0000000000..fe6e42f966
--- /dev/null
+++ b/langpacks/english/Plugins/Teams.txt
@@ -0,0 +1,81 @@
+#muuid {DCD56CEC-C61B-4275-a010-8c65c5848815}
+;============================================================
+; File: Teams.dll
+; Plugin: Teams protocol
+; Version: 0.96.6.1
+; Authors: Miranda NG team
+;============================================================
+[Microsoft Teams protocol support for Miranda NG.]
+;file \protocols\Teams\res\Resource.rc
+[Login:]
+[Default group:]
+[Account]
+[Teams]
+[Proceed]
+[Cancel]
+[Static]
+;file \protocols\Teams\src\main.cpp
+[Protocol icon]
+[Create new chat icon]
+[Block user icon]
+[Unblock user icon]
+[Incoming call icon]
+[Notification icon]
+[Error icon]
+[Action icon]
+;file \protocols\Teams\src\teams_chatrooms.cpp
+[Admin]
+[User]
+[Enter new nickname]
+[This chat is going to be destroyed forever with all its contents. This action cannot be undone. Are you sure?]
+[Warning]
+[&Invite user...]
+[&Leave chat session]
+[&Change topic...]
+[Kick &user]
+[Set &role]
+[&Admin]
+[&User]
+[Change nick...]
+[Enter new chatroom topic]
+;file \protocols\Teams\src\teams_contacts.cpp
+[(You)]
+[Are you sure?]
+;file \protocols\Teams\src\teams_login.cpp
+[To login into Teams you need to open '%S' in a browser and select your Teams account there.]
+[Enter the following code then: %s.]
+[Click Proceed to copy that code to clipboard and launch a browser]
+;file \protocols\Teams\src\teams_menus.cpp
+[Block contact]
+[Unblock contact]
+[Create new chat]
+[Set own mood]
+;file \protocols\Teams\src\teams_messages.cpp
+[Unknown error!]
+[Network error!]
+;file \protocols\Teams\src\teams_mood.cpp
+[None]
+[Custom emoji]
+[Be right back]
+[Out for lunch]
+[In meetings]
+[At school]
+[At the movies]
+[Traveling]
+[Celebrating]
+[Driving]
+[At the gym]
+[Working from home]
+;file \protocols\Teams\src\teams_options.cpp
+[Network]
+;file \protocols\Teams\src\teams_popups.cpp
+[Notifications]
+[Errors]
+[Calls]
+;file \protocols\Teams\src\teams_proto.cpp
+[%s websocket connection]
+[Incoming call]
+[Incoming call canceled]
+[Teams ID]
+;file \protocols\Teams\src\teams_utils.cpp
+[wrote]
diff --git a/langpacks/english/Plugins/Weather.txt b/langpacks/english/Plugins/Weather.txt
index e9ce0a352d..16a053ed18 100644
--- a/langpacks/english/Plugins/Weather.txt
+++ b/langpacks/english/Plugins/Weather.txt
@@ -92,6 +92,7 @@
[External Log]
[History Log]
[Variable List]
+[More Variables]
[Reset]
[Status Message]
[Sunset]
@@ -195,14 +196,26 @@
[Frame Background]
;file \protocols\Weather\src\weather_opt.cpp
[Weather Condition for %n as of %u]
-[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
-[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
+[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
+[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
[%n at %u: %c, %t (feel-like %f) Wind: %i %w Humidity: %m]
[%c, %t (feel-like %f) Wind: %i %w Humidity: %m]
[Temperature: %[Temperature]]
[%n (%u)]
[%c, %t\\nToday: High %h, Low %l]
-[%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temp\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set\n----------\n\\n\tnew line]
+[Current condition]
+[Current date]
+[Dewpoint]
+[Feel-like temp]
+[Today's high]
+[Wind direction]
+[Today's low]
+[Station name]
+[Station ID]
+[Update time]
+[Wind speed]
+[new line]
+[Here is a list of custom variables that are currently available]
[Weather Protocol Text Preview]
[Network]
[Account]
@@ -235,12 +248,11 @@
[Waxing gibbous]
[Waning gibbous]
[Waning crescent]
+[Date]
[Condition]
[High]
[Low]
[Moon phase]
-[Wind speed]
-[Wind direction]
[Feel]
;file \protocols\Weather\src\weather_userinfo.cpp
[Variable]
diff --git a/langpacks/russian/=CORE=.txt b/langpacks/russian/=CORE=.txt
index 8a4111e39d..39646e7e40 100644
--- a/langpacks/russian/=CORE=.txt
+++ b/langpacks/russian/=CORE=.txt
@@ -1,7 +1,7 @@
;============================================================
; File: miranda32/64.exe
; Module: Miranda Core
-; Version: 0.96.6
+; Version: 0.96.7
;============================================================
;file \src\core\stdautoaway\res\resource.rc
[Become idle if the following is left unattended:]
diff --git a/langpacks/russian/Langpack_russian.txt b/langpacks/russian/Langpack_russian.txt
index c8b681e538..02dc907fa1 100644
--- a/langpacks/russian/Langpack_russian.txt
+++ b/langpacks/russian/Langpack_russian.txt
@@ -15,7 +15,7 @@ Authors: Yevgeny, YurasiK, Yury A. Marcinchick
;============================================================
; File: miranda32/64.exe
; Module: Miranda Core
-; Version: 0.96.6
+; Version: 0.96.7
;============================================================
[Become idle if the following is left unattended:]
Перейти в режим ожидания, если неактивны:
@@ -2083,6 +2083,8 @@ Miranda не может распознать этот профиль
Прикрепить
[Code]
Код
+[Cloud file]
+Облачный файл
[&File]
&Файл
[File from %s]
@@ -9898,6 +9900,8 @@ ID записи
; Version: 0.1.0.11
; Authors: Miranda NG Team
;============================================================
+[Facebook Messenger protocol support for Miranda NG.]
+Поддержка протокола Facebook Messenger в Miranda NG.
[E-mail:]
Почта:
[Password:]
@@ -27934,6 +27938,107 @@ TabSRMM: %s
Загрузка схемы|Загрузка цветов и шрифтов может изменить настройки, установленные вашим скином.\n\nВы хотите продолжить?
[There are unsent messages waiting for confirmation.\nIf you close the window now, Miranda will try to send them but may be unable to inform you about possible delivery errors.\nDo you really want to close the window(s)?]
В очереди есть неотправленные сообщения, ждущие подтверждения.\nЕсли вы закроете окно, Miranda попытается отправить их, но не сможет предупредить о возможных ошибках.\nВы действительно хотите закрыть окно(а)?
+#muuid {DCD56CEC-C61B-4275-a010-8c65c5848815}
+;============================================================
+; File: Teams.dll
+; Plugin: Teams protocol
+; Version: 0.96.6.1
+; Authors: Miranda NG team
+;============================================================
+[Login:]
+Логин:
+[Default group:]
+Группа:
+[Account]
+Учётная запись
+[Teams]
+Teams
+[Proceed]
+Продолжить
+[Cancel]
+Отмена
+[Static]
+Статический
+[Protocol icon]
+Значок протокола
+[Create new chat icon]
+Создать новый чат
+[Block user icon]
+Заблокировать контакт
+[Unblock user icon]
+Разблокировать контакт
+[Incoming call icon]
+Значок входящего звонка
+[Notification icon]
+Значок уведомления
+[Error icon]
+Значок ошибки
+[Action icon]
+Значок действия
+[Admin]
+Админ
+[User]
+Пользователь
+[Enter new nickname]
+Введите новое имя
+[This chat is going to be destroyed forever with all its contents. This action cannot be undone. Are you sure?]
+Этот чат будет удалён со всеми входящими в него контактами. Это действие не может быть отменено. Вы уверены, что хотите продолжить?
+[Warning]
+Предупреждение
+[&Invite user...]
+&Пригласить пользователя...
+[&Leave chat session]
+&Покинуть чат
+[&Change topic...]
+&Изменить тему...
+[Kick &user]
+&Выкинуть пользователя
+[Set &role]
+Установить &роль
+[&Admin]
+&Админ
+[&User]
+&Пользователь
+[Change nick...]
+Изменить имя...
+[Enter new chatroom topic]
+Введите новую тему комнаты чата
+[(You)]
+(Вы)
+[Are you sure?]
+Вы уверены?
+[Block contact]
+Заблокировать контакт
+[Unblock contact]
+Разблокировать контакт
+[Create new chat]
+Создать новый чат
+[Unknown error!]
+Неизвестная ошибка!
+[Network error!]
+Ошибка сети!
+[None]
+Нет
+[Traveling]
+В дороге
+[Driving]
+за рулём
+[Network]
+Сеть
+[Notifications]
+Уведомления
+[Errors]
+Ошибки
+[Calls]
+Звонки
+[Incoming call]
+Входящий звонок
+[Incoming call canceled]
+Отмена входящего звонка
+[Teams ID]
+Teams ID
+[wrote]
+писал(а)
#muuid {ae708252-0df8-42ba-9ef9-9acc038eeda7}
;============================================================
; File: Telegram.dll
@@ -27985,6 +28090,8 @@ TabSRMM: %s
Добавить реакцию
[Birthday]
День рождения
+[Biography]
+О себе
[Enter email address for account verification]
Введите адрес электронной почты для подтверждения учётной записи
[Enter verification code received by email]
@@ -30211,11 +30318,13 @@ Web Дизайн
[an anniversary]
праздник
[He]
-Ему
+Он
[She]
-Ей
+Она
[He/she]
-Ему/ей
+Он/она
+[%s has the following anniversaries:]
+%s отмечает следующие годовщины:
[%d. %s today]
%d лет. %s сегодня
[%d. %s tomorrow]
@@ -30228,6 +30337,8 @@ Web Дизайн
%s завтра отмечает день рождения.
[%s has birthday in %d days.]
%s отмечает день рождения через %d дней.
+[%s becomes %d years old.]
+%s отмечает %d день рождения.
[No anniversaries to remind of]
Нет праздников, о которых надо уведомлять
[Reminders]
@@ -30915,6 +31026,8 @@ URL:
Ошибка
[Error %d. Data will not be sent or received.]
Ошибка %d. Данные не будут приняты или отправлены.
+[Captcha processing error.]
+Ошибка при обработке капчи.
[You have to validate your account before you can use VK in Miranda NG]
Вы должны подтвердить свою учётную запись перед использованием протокола ВКонтакте в Miranda NG
[One of the parameters specified was missing or invalid]
@@ -32435,6 +32548,8 @@ Miranda NG прекрасна, а это - пример длинного соо
Просмотр истории
[Variable List]
Переменные
+[More Variables]
+Ещё переменные
[Reset]
Сброс
[Status Message]
@@ -32625,10 +32740,10 @@ INI файл погоды для этой станции не найден (20)
Фон фрейма
[Weather Condition for %n as of %u]
Состояние погоды для %n на %u
-[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
-Ощущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]
-[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
-%c\\nТемпература: %t\\nОщущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]
+[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
+Ощущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]
+[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
+%c\\nТемпература: %t\\nОщущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]
[%n at %u: %c, %t (feel-like %f) Wind: %i %w Humidity: %m]
%n в %u: %c, %t (ощущается %f) Ветер: %i %w Влажность: %m
[%c, %t (feel-like %f) Wind: %i %w Humidity: %m]
@@ -32639,8 +32754,32 @@ INI файл погоды для этой станции не найден (20)
%n (%u)
[%c, %t\\nToday: High %h, Low %l]
%c, %t\\nСегодня: макс. %h, мин. %l
-[%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temp\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set\n----------\n\\n\tnew line]
-%c прогноз на сегодня\n%d текущая дата\n%e роса\n%f ощущение\n%h максимум\n%i направление ветра\n%l минимум\n%m влажность\n%n город\n%p давление\n%r восход\n%s ID города\n%t температура\n%u обновление\n%v видимость\n%w скорость ветра\n%y закат\n----------\n\\n новая строка
+[Current condition]
+Текущее состояние
+[Current date]
+Текущая дата
+[Dewpoint]
+Роса
+[Feel-like temp]
+Ощущение
+[Today's high]
+Максимум
+[Wind direction]
+Ветер: направление
+[Today's low]
+Минимум
+[Station name]
+Название станции
+[Station ID]
+ID города
+[Update time]
+Обновление
+[Wind speed]
+Ветер: скорость
+[new line]
+новая строка
+[Here is a list of custom variables that are currently available]
+Список дополнительных переменных, доступных на данный момент
[Weather Protocol Text Preview]
Просмотр протокола погоды
[Network]
@@ -32697,6 +32836,8 @@ INI файл погоды для этой станции не найден (20)
Убывающая Луна
[Waning crescent]
Убывающая Луна
+[Date]
+Дата
[Condition]
Условия
[High]
@@ -32705,10 +32846,6 @@ INI файл погоды для этой станции не найден (20)
Низкий
[Moon phase]
Фаза луны
-[Wind speed]
-Ветер: скорость
-[Wind direction]
-Ветер: направление
[Feel]
Ощущение
[Variable]
@@ -32819,7 +32956,7 @@ INI файл погоды для этой станции не найден (20)
[Clear / Wind]
Ясно / ветер
[Clear]
-Очистить
+Ясно
[Clouds Early / Clearing Late]
С утра облачно / ясно во второй половине дня
[Cloudy / Wind]
diff --git a/langpacks/russian/Plugins/Folders.txt b/langpacks/russian/Plugins/Folders.txt
index fa31e9ec91..693d719b24 100644
--- a/langpacks/russian/Plugins/Folders.txt
+++ b/langpacks/russian/Plugins/Folders.txt
@@ -11,7 +11,7 @@
[Custom folders]
Папки
[Preview]
-Тест
+Предпросмотр
[Edit]
Изменить
[Refresh preview]
diff --git a/langpacks/russian/Plugins/Teams.txt b/langpacks/russian/Plugins/Teams.txt
new file mode 100644
index 0000000000..45867fcc2f
--- /dev/null
+++ b/langpacks/russian/Plugins/Teams.txt
@@ -0,0 +1,143 @@
+#muuid {DCD56CEC-C61B-4275-a010-8c65c5848815}
+;============================================================
+; File: Teams.dll
+; Plugin: Teams protocol
+; Version: 0.96.6.1
+; Authors: Miranda NG team
+;============================================================
+[Microsoft Teams protocol support for Miranda NG.]
+Поддержка протокола Microsoft Teams в Miranda NG.
+;file \protocols\Teams\res\Resource.rc
+[Login:]
+Логин:
+[Default group:]
+Группа:
+[Account]
+Учётная запись
+[Teams]
+Teams
+[Proceed]
+Продолжить
+[Cancel]
+Отмена
+[Static]
+Статический
+;file \protocols\Teams\src\main.cpp
+[Protocol icon]
+Значок протокола
+[Create new chat icon]
+Создать новый чат
+[Block user icon]
+Заблокировать контакт
+[Unblock user icon]
+Разблокировать контакт
+[Incoming call icon]
+Значок входящего звонка
+[Notification icon]
+Значок уведомления
+[Error icon]
+Значок ошибки
+[Action icon]
+Значок действия
+;file \protocols\Teams\src\teams_chatrooms.cpp
+[Admin]
+Админ
+[User]
+Пользователь
+[Enter new nickname]
+Введите новое имя
+[This chat is going to be destroyed forever with all its contents. This action cannot be undone. Are you sure?]
+Этот чат будет удалён со всеми входящими в него контактами. Это действие не может быть отменено. Вы уверены, что хотите продолжить?
+[Warning]
+Предупреждение
+[&Invite user...]
+&Пригласить пользователя...
+[&Leave chat session]
+&Покинуть чат
+[&Change topic...]
+&Изменить тему...
+[Kick &user]
+&Выкинуть пользователя
+[Set &role]
+Установить &роль
+[&Admin]
+&Админ
+[&User]
+&Пользователь
+[Change nick...]
+Изменить имя...
+[Enter new chatroom topic]
+Введите новую тему комнаты чата
+;file \protocols\Teams\src\teams_contacts.cpp
+[(You)]
+(Вы)
+[Are you sure?]
+Вы уверены?
+;file \protocols\Teams\src\teams_login.cpp
+[To login into Teams you need to open '%S' in a browser and select your Teams account there.]
+
+[Enter the following code then: %s.]
+
+[Click Proceed to copy that code to clipboard and launch a browser]
+
+;file \protocols\Teams\src\teams_menus.cpp
+[Block contact]
+Заблокировать контакт
+[Unblock contact]
+Разблокировать контакт
+[Create new chat]
+Создать новый чат
+[Set own mood]
+
+;file \protocols\Teams\src\teams_messages.cpp
+[Unknown error!]
+Неизвестная ошибка!
+[Network error!]
+Ошибка сети!
+;file \protocols\Teams\src\teams_mood.cpp
+[None]
+Нет
+[Custom emoji]
+
+[Be right back]
+
+[Out for lunch]
+
+[In meetings]
+
+[At school]
+
+[At the movies]
+
+[Traveling]
+В дороге
+[Celebrating]
+
+[Driving]
+за рулём
+[At the gym]
+
+[Working from home]
+
+;file \protocols\Teams\src\teams_options.cpp
+[Network]
+Сеть
+;file \protocols\Teams\src\teams_popups.cpp
+[Notifications]
+Уведомления
+[Errors]
+Ошибки
+[Calls]
+Звонки
+;file \protocols\Teams\src\teams_proto.cpp
+[%s websocket connection]
+
+[Incoming call]
+Входящий звонок
+[Incoming call canceled]
+Отмена входящего звонка
+[Teams ID]
+Teams ID
+;file \protocols\Teams\src\teams_utils.cpp
+[wrote]
+писал(а)
diff --git a/langpacks/russian/Plugins/Weather.txt b/langpacks/russian/Plugins/Weather.txt
index ab4e43cb11..5db583872c 100644
--- a/langpacks/russian/Plugins/Weather.txt
+++ b/langpacks/russian/Plugins/Weather.txt
@@ -178,6 +178,8 @@
Просмотр истории
[Variable List]
Переменные
+[More Variables]
+Ещё переменные
[Reset]
Сброс
[Status Message]
@@ -376,10 +378,10 @@ INI файл погоды для этой станции не найден (20)
;file \protocols\Weather\src\weather_opt.cpp
[Weather Condition for %n as of %u]
Состояние погоды для %n на %u
-[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
-Ощущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]
-[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]]
-%c\\nТемпература: %t\\nОщущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]
+[Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
+Ощущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]
+[%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]]
+%c\\nТемпература: %t\\nОщущение: %f\\nДавление: %p\\nВетер: %i %w\\nВлажность: %m\\nРоса: %e\\nВидимость: %v\\n\\nВосход: %r\\nЗакат: %y\\n\\nПрогноз на 5 дней:\\n\\n%[Forecast Day 1]\\n\\n%[Forecast Day 2]\\n\\n%[Forecast Day 3]\\n\\n%[Forecast Day 4]\\n\\n%[Forecast Day 5]
[%n at %u: %c, %t (feel-like %f) Wind: %i %w Humidity: %m]
%n в %u: %c, %t (ощущается %f) Ветер: %i %w Влажность: %m
[%c, %t (feel-like %f) Wind: %i %w Humidity: %m]
@@ -390,8 +392,32 @@ INI файл погоды для этой станции не найден (20)
%n (%u)
[%c, %t\\nToday: High %h, Low %l]
%c, %t\\nСегодня: макс. %h, мин. %l
-[%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temp\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set\n----------\n\\n\tnew line]
-%c прогноз на сегодня\n%d текущая дата\n%e роса\n%f ощущение\n%h максимум\n%i направление ветра\n%l минимум\n%m влажность\n%n город\n%p давление\n%r восход\n%s ID города\n%t температура\n%u обновление\n%v видимость\n%w скорость ветра\n%y закат\n----------\n\\n новая строка
+[Current condition]
+Текущее состояние
+[Current date]
+Текущая дата
+[Dewpoint]
+Роса
+[Feel-like temp]
+Ощущение
+[Today's high]
+Максимум
+[Wind direction]
+Ветер: направление
+[Today's low]
+Минимум
+[Station name]
+Название станции
+[Station ID]
+ID города
+[Update time]
+Обновление
+[Wind speed]
+Ветер: скорость
+[new line]
+новая строка
+[Here is a list of custom variables that are currently available]
+Список дополнительных переменных, доступных на данный момент
[Weather Protocol Text Preview]
Просмотр протокола погоды
[Network]
@@ -452,6 +478,8 @@ INI файл погоды для этой станции не найден (20)
Убывающая Луна
[Waning crescent]
Убывающая Луна
+[Date]
+Дата
[Condition]
Условия
[High]
@@ -460,10 +488,6 @@ INI файл погоды для этой станции не найден (20)
Низкий
[Moon phase]
Фаза луны
-[Wind speed]
-Ветер: скорость
-[Wind direction]
-Ветер: направление
[Feel]
Ощущение
;file \protocols\Weather\src\weather_userinfo.cpp
diff --git a/langpacks/russian/Untranslated/Teams.txt b/langpacks/russian/Untranslated/Teams.txt
new file mode 100644
index 0000000000..6aaa2d858d
--- /dev/null
+++ b/langpacks/russian/Untranslated/Teams.txt
@@ -0,0 +1,14 @@
+[To login into Teams you need to open '%S' in a browser and select your Teams account there.]
+[Enter the following code then: %s.]
+[Click Proceed to copy that code to clipboard and launch a browser]
+[Set own mood]
+[Custom emoji]
+[Be right back]
+[Out for lunch]
+[In meetings]
+[At school]
+[At the movies]
+[Celebrating]
+[At the gym]
+[Working from home]
+[%s websocket connection]
diff --git a/langpacks/russian/Weather/Weather_forecast.txt b/langpacks/russian/Weather/Weather_forecast.txt
index d77c91dc2d..1d05bdc425 100644
--- a/langpacks/russian/Weather/Weather_forecast.txt
+++ b/langpacks/russian/Weather/Weather_forecast.txt
@@ -92,7 +92,7 @@
[Clear / Wind]
Ясно / ветер
[Clear]
-Очистить
+Ясно
[Clouds Early / Clearing Late]
С утра облачно / ясно во второй половине дня
[Cloudy / Wind]
diff --git a/libs/win32/mir_app.lib b/libs/win32/mir_app.lib
index fcad461a27..88414ff374 100644
--- a/libs/win32/mir_app.lib
+++ b/libs/win32/mir_app.lib
Binary files differ
diff --git a/libs/win64/mir_app.lib b/libs/win64/mir_app.lib
index 36010d730a..0eb7e4ede8 100644
--- a/libs/win64/mir_app.lib
+++ b/libs/win64/mir_app.lib
Binary files differ
diff --git a/plugins/TabSRMM/src/msgdlgother.cpp b/plugins/TabSRMM/src/msgdlgother.cpp
index 56cdf162d7..346c758369 100644
--- a/plugins/TabSRMM/src/msgdlgother.cpp
+++ b/plugins/TabSRMM/src/msgdlgother.cpp
@@ -992,7 +992,7 @@ void CMsgDialog::HandlePasteAndSend()
m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
if (GetWindowTextLength(m_message.GetHwnd()) > 0)
- SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+ m_btnOk.Click();
}
/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/CloudFile/res/dropbox.ico b/protocols/CloudFile/res/dropbox.ico
index 8a286d7f32..09d2721244 100644
--- a/protocols/CloudFile/res/dropbox.ico
+++ b/protocols/CloudFile/res/dropbox.ico
Binary files differ
diff --git a/protocols/CloudFile/res/gdrive.ico b/protocols/CloudFile/res/gdrive.ico
index a34123e95b..3655f72583 100644
--- a/protocols/CloudFile/res/gdrive.ico
+++ b/protocols/CloudFile/res/gdrive.ico
Binary files differ
diff --git a/protocols/CloudFile/res/onedrive.ico b/protocols/CloudFile/res/onedrive.ico
index 502cee9bb7..94c8ab8240 100644
--- a/protocols/CloudFile/res/onedrive.ico
+++ b/protocols/CloudFile/res/onedrive.ico
Binary files differ
diff --git a/protocols/CloudFile/res/upload.ico b/protocols/CloudFile/res/upload.ico
index b51e87ed35..09f224a20e 100644
--- a/protocols/CloudFile/res/upload.ico
+++ b/protocols/CloudFile/res/upload.ico
Binary files differ
diff --git a/protocols/CloudFile/res/yadisk.ico b/protocols/CloudFile/res/yadisk.ico
index b3a5ac69b4..31d802c51f 100644
--- a/protocols/CloudFile/res/yadisk.ico
+++ b/protocols/CloudFile/res/yadisk.ico
Binary files differ
diff --git a/protocols/Discord/proto_discord/res/Away.ico b/protocols/Discord/proto_discord/res/Away.ico
index 844c1d4b3a..46dccb19ca 100644
--- a/protocols/Discord/proto_discord/res/Away.ico
+++ b/protocols/Discord/proto_discord/res/Away.ico
Binary files differ
diff --git a/protocols/Discord/proto_discord/res/DND.ico b/protocols/Discord/proto_discord/res/DND.ico
index 6341c0e08c..805dd75622 100644
--- a/protocols/Discord/proto_discord/res/DND.ico
+++ b/protocols/Discord/proto_discord/res/DND.ico
Binary files differ
diff --git a/protocols/Discord/proto_discord/res/Invisible.ico b/protocols/Discord/proto_discord/res/Invisible.ico
index 7d34d4ca58..d168845ae6 100644
--- a/protocols/Discord/proto_discord/res/Invisible.ico
+++ b/protocols/Discord/proto_discord/res/Invisible.ico
Binary files differ
diff --git a/protocols/Discord/proto_discord/res/Offline.ico b/protocols/Discord/proto_discord/res/Offline.ico
index f2ec365064..7473a3b968 100644
--- a/protocols/Discord/proto_discord/res/Offline.ico
+++ b/protocols/Discord/proto_discord/res/Offline.ico
Binary files differ
diff --git a/protocols/Discord/proto_discord/res/Online.ico b/protocols/Discord/proto_discord/res/Online.ico
index 94f4d0d8bd..1d3efde242 100644
--- a/protocols/Discord/proto_discord/res/Online.ico
+++ b/protocols/Discord/proto_discord/res/Online.ico
Binary files differ
diff --git a/protocols/Discord/res/discord.ico b/protocols/Discord/res/discord.ico
index c2830ed132..1d3efde242 100644
--- a/protocols/Discord/res/discord.ico
+++ b/protocols/Discord/res/discord.ico
Binary files differ
diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp
index c038234aec..e3bf1b307c 100644
--- a/protocols/Discord/src/gateway.cpp
+++ b/protocols/Discord/src/gateway.cpp
@@ -25,9 +25,7 @@ bool CDiscordProto::GatewaySend(const JSONNode &pRoot)
if (!m_bConnected)
return false;
- json_string szText = pRoot.write();
- debugLogA("Gateway send: %s", szText.c_str());
- m_ws.sendText(szText.c_str());
+ m_ws.sendText(pRoot.write().c_str());
return true;
}
diff --git a/protocols/Discord/src/voice_client.cpp b/protocols/Discord/src/voice_client.cpp
index 1e20a93723..99d7668d74 100644
--- a/protocols/Discord/src/voice_client.cpp
+++ b/protocols/Discord/src/voice_client.cpp
@@ -61,9 +61,7 @@ void CDiscordVoiceCall::write(int op, JSONNode &d)
JSONNode payload;
payload << INT_PARAM("op", op) << d;
-
auto json = payload.write();
- ppro->debugLogA("Voice JSON sent: %s", json.c_str());
mir_cslock lck(m_cs);
m_ws->sendText(json.c_str());
diff --git a/protocols/ICQCorp/CMakeLists.txt b/protocols/ICQCorp/CMakeLists.txt
index 9e3d58ba26..d18898a34e 100644
--- a/protocols/ICQCorp/CMakeLists.txt
+++ b/protocols/ICQCorp/CMakeLists.txt
@@ -12,4 +12,5 @@ file(GLOB SOURCES "src/*.h" "res/*.rc"
"src/user.cpp"
)
set(TARGET ICQCorp)
-include(${CMAKE_SOURCE_DIR}/cmake/plugin.cmake) \ No newline at end of file
+include(${CMAKE_SOURCE_DIR}/cmake/plugin.cmake)
+add_subdirectory(proto_icq)
diff --git a/protocols/ICQCorp/Proto_icq/CMakeLists.txt b/protocols/ICQCorp/Proto_icq/CMakeLists.txt
new file mode 100644
index 0000000000..596ac21624
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/CMakeLists.txt
@@ -0,0 +1,2 @@
+set(TARGET Proto_ICQ)
+include(${CMAKE_SOURCE_DIR}/cmake/icons.cmake) \ No newline at end of file
diff --git a/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj b/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj
new file mode 100644
index 0000000000..21763bbc0b
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Proto_ICQ</ProjectName>
+ <ProjectGuid>{DB3B0449-E576-4BBB-8B08-AB9E914D39CA}</ProjectGuid>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(ProjectDir)..\..\..\build\vc.common\icons.props" />
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj.filters b/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj.filters
new file mode 100644
index 0000000000..28f81e7f1b
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/Proto_ICQ.vcxproj.filters
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(ProjectDir)..\..\..\build\vc.common\common.filters" />
+</Project> \ No newline at end of file
diff --git a/protocols/ICQCorp/Proto_icq/res/Away.ico b/protocols/ICQCorp/Proto_icq/res/Away.ico
new file mode 100644
index 0000000000..248a3e9916
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Away.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/DND.ico b/protocols/ICQCorp/Proto_icq/res/DND.ico
new file mode 100644
index 0000000000..4833160eac
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/DND.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/FFC.ico b/protocols/ICQCorp/Proto_icq/res/FFC.ico
new file mode 100644
index 0000000000..e7ec4d3ae2
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/FFC.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Invisible.ico b/protocols/ICQCorp/Proto_icq/res/Invisible.ico
new file mode 100644
index 0000000000..6a337c2926
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Invisible.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/NA.ico b/protocols/ICQCorp/Proto_icq/res/NA.ico
new file mode 100644
index 0000000000..ec0621dc9f
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/NA.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Occupied.ico b/protocols/ICQCorp/Proto_icq/res/Occupied.ico
new file mode 100644
index 0000000000..04ea2a5855
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Occupied.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Offline.ico b/protocols/ICQCorp/Proto_icq/res/Offline.ico
new file mode 100644
index 0000000000..af862168cd
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Offline.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Online.ico b/protocols/ICQCorp/Proto_icq/res/Online.ico
new file mode 100644
index 0000000000..2e33305a76
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Online.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Phone.ico b/protocols/ICQCorp/Proto_icq/res/Phone.ico
new file mode 100644
index 0000000000..74c80b66ed
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Phone.ico
Binary files differ
diff --git a/protocols/ICQCorp/Proto_icq/res/Proto_ICQ.rc b/protocols/ICQCorp/Proto_icq/res/Proto_ICQ.rc
new file mode 100644
index 0000000000..33d8faf243
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/res/Proto_ICQ.rc
@@ -0,0 +1,77 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "..\src\resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Russian (Russia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "..\\src\\resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "Offline.ico"
+IDI_ICON2 ICON "Online.ico"
+IDI_ICON3 ICON "Away.ico"
+IDI_ICON4 ICON "Invisible.ico"
+IDI_ICON5 ICON "NA.ico"
+IDI_ICON6 ICON "DND.ico"
+IDI_ICON7 ICON "Occupied.ico"
+IDI_ICON8 ICON "FFC.ico"
+IDI_ICON9 ICON "Phone.ico"
+#endif // Russian (Russia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/protocols/ICQCorp/Proto_icq/src/resource.h b/protocols/ICQCorp/Proto_icq/src/resource.h
new file mode 100644
index 0000000000..c74e04f59e
--- /dev/null
+++ b/protocols/ICQCorp/Proto_icq/src/resource.h
@@ -0,0 +1,24 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Proto_ICQ.rc
+//
+#define IDI_ICON1 105
+#define IDI_ICON2 104
+#define IDI_ICON3 128
+#define IDI_ICON4 130
+#define IDI_ICON5 131
+#define IDI_ICON6 158
+#define IDI_ICON7 159
+#define IDI_ICON8 129
+#define IDI_ICON9 1002
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 110
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/protocols/SkypeWeb/SkypeWeb.vcxproj b/protocols/SkypeWeb/SkypeWeb.vcxproj
index f44147ab8f..c7cda08954 100644
--- a/protocols/SkypeWeb/SkypeWeb.vcxproj
+++ b/protocols/SkypeWeb/SkypeWeb.vcxproj
@@ -57,7 +57,6 @@
<ClInclude Include="src\requests\history.h" />
<ClInclude Include="src\requests\login.h" />
<ClInclude Include="src\requests\oauth.h" />
- <ClInclude Include="src\requests\poll.h" />
<ClInclude Include="src\requests\profile.h" />
<ClInclude Include="src\requests\search.h" />
<ClInclude Include="src\requests\status.h" />
diff --git a/protocols/SkypeWeb/SkypeWeb.vcxproj.filters b/protocols/SkypeWeb/SkypeWeb.vcxproj.filters
index e6d0bb2fc3..15d9199310 100644
--- a/protocols/SkypeWeb/SkypeWeb.vcxproj.filters
+++ b/protocols/SkypeWeb/SkypeWeb.vcxproj.filters
@@ -96,9 +96,6 @@
<ClInclude Include="src\requests\oauth.h">
<Filter>Header Files\Requests</Filter>
</ClInclude>
- <ClInclude Include="src\requests\poll.h">
- <Filter>Header Files\Requests</Filter>
- </ClInclude>
<ClInclude Include="src\requests\profile.h">
<Filter>Header Files\Requests</Filter>
</ClInclude>
diff --git a/protocols/SkypeWeb/src/requests/poll.h b/protocols/SkypeWeb/src/requests/poll.h
deleted file mode 100644
index bf1850fd27..0000000000
--- a/protocols/SkypeWeb/src/requests/poll.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright (c) 2015-25 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
-as published by the Free Software Foundation version 2
-of the License.
-
-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, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_POLL_H_
-#define _SKYPE_POLL_H_
-
-struct PollRequest : public AsyncHttpRequest
-{
- PollRequest(CSkypeProto *ppro) :
- AsyncHttpRequest(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(ppro->m_szId) + "/subscriptions/0/poll")
- {
- flags |= NLHRF_PERSISTENT;
- timeout = 120000;
-
- if (ppro->m_iPollingId != -1)
- m_szUrl.AppendFormat("?ackId=%d", ppro->m_iPollingId);
-
- AddHeader("Referer", "https://web.skype.com/main");
- AddHeader("ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=swx-skype.com; clientVer=908/1.85.0.29");
- AddHeader("Accept", "application/json");
- AddHeader("Accept-Language", "en, C");
- }
-};
-#endif //_SKYPE_POLL_H_ \ No newline at end of file
diff --git a/protocols/SkypeWeb/src/skype_polling.cpp b/protocols/SkypeWeb/src/skype_polling.cpp
index ed7b50938e..2821f06d34 100644
--- a/protocols/SkypeWeb/src/skype_polling.cpp
+++ b/protocols/SkypeWeb/src/skype_polling.cpp
@@ -27,14 +27,34 @@ void CSkypeProto::PollingThread(void *)
if (m_isTerminated || m_szId == nullptr)
break;
- std::unique_ptr<PollRequest> request(new PollRequest(this));
- request->nlc = m_hPollingConn;
- NLHR_PTR response(DoSend(request.get()));
+ AsyncHttpRequest req(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(m_szId) + "/subscriptions/0/poll");
+ req.flags |= NLHRF_PERSISTENT;
+ req.timeout = 120000;
+ req.nlc = m_hPollingConn;
+
+ if (m_iPollingId != -1)
+ req.m_szUrl.AppendFormat("?ackId=%d", m_iPollingId);
+
+ req.AddHeader("Referer", "https://web.skype.com/main");
+ req.AddHeader("ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=swx-skype.com; clientVer=908/1.85.0.29");
+ req.AddHeader("Accept", "application/json");
+ req.AddHeader("Accept-Language", "en, C");
+
+ NLHR_PTR response(DoSend(&req));
if (m_isTerminated || m_szId == nullptr)
break;
- if (response == nullptr || response->resultCode != 200) {
- m_hPollingConn = nullptr;
+ // no network?..
+ if (response == nullptr)
+ break;
+
+ if (response->resultCode != 200) {
+ auto reply = JSONNode::parse(response->body);
+ if (reply && reply["message"]["errorCode"] == 729) // endpoint broken, log off
+ break;
+
+ Sleep(200);
+ m_hPollingConn = response->nlc;
continue;
}
diff --git a/protocols/SkypeWeb/src/stdafx.h b/protocols/SkypeWeb/src/stdafx.h
index ef94f3040c..263aefd914 100644
--- a/protocols/SkypeWeb/src/stdafx.h
+++ b/protocols/SkypeWeb/src/stdafx.h
@@ -119,7 +119,6 @@ struct AsyncHttpRequest : public MTHttpRequest<CSkypeProto>
#include "requests/history.h"
#include "requests/login.h"
#include "requests/oauth.h"
-#include "requests/poll.h"
#include "requests/profile.h"
#include "requests/search.h"
#include "requests/status.h"
diff --git a/protocols/Steam/proto_steam/res/Invisible.ico b/protocols/Steam/proto_steam/res/Invisible.ico
index f920c04ed2..68e4a93641 100644
--- a/protocols/Steam/proto_steam/res/Invisible.ico
+++ b/protocols/Steam/proto_steam/res/Invisible.ico
Binary files differ
diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp
index 1235130542..669d164351 100644
--- a/protocols/Steam/src/steam_login.cpp
+++ b/protocols/Steam/src/steam_login.cpp
@@ -159,7 +159,7 @@ INT_PTR CALLBACK CSteamProto::EnterEmailCode(void *param)
ppro->SendConfirmationCode(true, T2Utf(es.ptszResult));
mir_free(es.ptszResult);
}
- else ppro->Logout();
+ else ppro->m_ws->terminate();
return 0;
}
@@ -174,7 +174,7 @@ INT_PTR CALLBACK CSteamProto::EnterTotpCode(void *param)
ppro->SendConfirmationCode(false, T2Utf(es.ptszResult));
mir_free(es.ptszResult);
}
- else ppro->Logout();
+ else ppro->m_ws->terminate();
return 0;
}
diff --git a/protocols/Teams/Teams.vcxproj b/protocols/Teams/Teams.vcxproj
index ad579a582d..436f8c746d 100644
--- a/protocols/Teams/Teams.vcxproj
+++ b/protocols/Teams/Teams.vcxproj
@@ -42,11 +42,11 @@
<ClCompile Include="src\teams_messages.cpp" />
<ClCompile Include="src\teams_mood.cpp" />
<ClCompile Include="src\teams_options.cpp" />
- <ClCompile Include="src\teams_polling.cpp" />
<ClCompile Include="src\teams_popups.cpp" />
<ClCompile Include="src\teams_profile.cpp" />
<ClCompile Include="src\teams_proto.cpp" />
<ClCompile Include="src\teams_search.cpp" />
+ <ClCompile Include="src\teams_trouter.cpp" />
<ClCompile Include="src\teams_utils.cpp" />
<ClInclude Include="src\resource.h" />
<ClInclude Include="src\stdafx.h" />
diff --git a/protocols/Teams/Teams.vcxproj.filters b/protocols/Teams/Teams.vcxproj.filters
index b2582eb02b..c15ff6085c 100644
--- a/protocols/Teams/Teams.vcxproj.filters
+++ b/protocols/Teams/Teams.vcxproj.filters
@@ -43,9 +43,6 @@
<ClCompile Include="src\teams_mood.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\teams_polling.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\teams_popups.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -61,6 +58,9 @@
<ClCompile Include="src\teams_search.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\teams_trouter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\resource.h">
diff --git a/protocols/Teams/proto_teams/res/Away.ico b/protocols/Teams/proto_teams/res/Away.ico
index 2faf6635bd..a6b544e597 100644
--- a/protocols/Teams/proto_teams/res/Away.ico
+++ b/protocols/Teams/proto_teams/res/Away.ico
Binary files differ
diff --git a/protocols/Teams/proto_teams/res/DND.ico b/protocols/Teams/proto_teams/res/DND.ico
index b38ab27226..5919b2e563 100644
--- a/protocols/Teams/proto_teams/res/DND.ico
+++ b/protocols/Teams/proto_teams/res/DND.ico
Binary files differ
diff --git a/protocols/Teams/proto_teams/res/Invisible.ico b/protocols/Teams/proto_teams/res/Invisible.ico
index 49b66a79fe..673f13a81a 100644
--- a/protocols/Teams/proto_teams/res/Invisible.ico
+++ b/protocols/Teams/proto_teams/res/Invisible.ico
Binary files differ
diff --git a/protocols/Teams/src/main.cpp b/protocols/Teams/src/main.cpp
index d2ac241040..07778b2405 100644
--- a/protocols/Teams/src/main.cpp
+++ b/protocols/Teams/src/main.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (C) 2025 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
+as published by the Free Software Foundation version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
CMPlugin g_plugin;
diff --git a/protocols/Teams/src/requests/capabilities.h b/protocols/Teams/src/requests/capabilities.h
deleted file mode 100644
index 566d946e3e..0000000000
--- a/protocols/Teams/src/requests/capabilities.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-Copyright (c) 2015-25 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
-as published by the Free Software Foundation version 2
-of the License.
-
-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, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_REQUEST_CAPS_H_
-#define _SKYPE_REQUEST_CAPS_H_
-
-struct SendCapabilitiesRequest : public AsyncHttpRequest
-{
- SendCapabilitiesRequest(const char *hostname, CTeamsProto *ppro) :
- AsyncHttpRequest(REQUEST_PUT, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(ppro->m_szId) + "/presenceDocs/messagingService", &CTeamsProto::OnCapabilitiesSended)
- {
- JSONNode privateInfo; privateInfo.set_name("privateInfo");
- privateInfo << CHAR_PARAM("epname", hostname);
-
- JSONNode publicInfo; publicInfo.set_name("publicInfo");
- publicInfo << CHAR_PARAM("capabilities", "Audio|Video") << INT_PARAM("typ", 125)
- << CHAR_PARAM("skypeNameVersion", "Miranda NG Skype") << CHAR_PARAM("nodeInfo", "xx") << CHAR_PARAM("version", g_szMirVer);
-
- JSONNode node;
- node << CHAR_PARAM("id", "messagingService") << CHAR_PARAM("type", "EndpointPresenceDoc")
- << CHAR_PARAM("selfLink", "uri") << privateInfo << publicInfo;
-
- m_szParam = node.write().c_str();
- }
-};
-
-#endif //_SKYPE_REQUEST_CAPS_H_
diff --git a/protocols/Teams/src/requests/contacts.h b/protocols/Teams/src/requests/contacts.h
deleted file mode 100644
index f0614f10b6..0000000000
--- a/protocols/Teams/src/requests/contacts.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright (c) 2015-25 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
-as published by the Free Software Foundation version 2
-of the License.
-
-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, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_REQUEST_CONTACTS_H_
-#define _SKYPE_REQUEST_CONTACTS_H_
-
-struct GetContactListRequest : public AsyncHttpRequest
-{
- GetContactListRequest() :
- AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/contacts", &CTeamsProto::LoadContactList)
- {
- }
-};
-
-struct GetContactsAuthRequest : public AsyncHttpRequest
-{
- GetContactsAuthRequest() :
- AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/invites", &CTeamsProto::LoadContactsAuth)
- {
- }
-};
-
-struct AddContactRequest : public AsyncHttpRequest
-{
- AddContactRequest(const char *who, const char *greeting = "") :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts")
- {
- JSONNode node;
- node << CHAR_PARAM("mri", who) << CHAR_PARAM("greeting", greeting);
- m_szParam = node.write().c_str();
- }
-};
-
-struct AuthAcceptRequest : public AsyncHttpRequest
-{
- AuthAcceptRequest(const char *who) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS)
- {
- m_szUrl.AppendFormat("/users/SELF/invites/%s/accept", mir_urlEncode(who).c_str());
- }
-};
-
-struct AuthDeclineRequest : public AsyncHttpRequest
-{
- AuthDeclineRequest(const char *who) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS)
- {
- m_szUrl.AppendFormat("/users/SELF/invites/%s/decline", mir_urlEncode(who).c_str());
- }
-};
-
-struct BlockContactRequest : public AsyncHttpRequest
-{
- BlockContactRequest(CTeamsProto *ppro, MCONTACT hContact) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + ppro->getId(hContact), &CTeamsProto::OnBlockContact)
- {
- m_szParam = "{\"report_abuse\":\"false\",\"ui_version\":\"skype.com\"}";
- pUserInfo = (void *)hContact;
- }
-};
-
-struct UnblockContactRequest : public AsyncHttpRequest
-{
- UnblockContactRequest(CTeamsProto *ppro, MCONTACT hContact) :
- AsyncHttpRequest(REQUEST_DELETE, HOST_CONTACTS, 0, &CTeamsProto::OnUnblockContact)
- {
- m_szUrl.AppendFormat("/users/SELF/contacts/blocklist/%s", ppro->getId(hContact).c_str());
- pUserInfo = (void *)hContact;
-
- // TODO: user ip address
- this << CHAR_PARAM("reporterIp", "123.123.123.123") << CHAR_PARAM("uiVersion", g_szMirVer);
- }
-};
-
-#endif //_SKYPE_REQUEST_CONTACTS_H_ \ No newline at end of file
diff --git a/protocols/Teams/src/requests/poll.h b/protocols/Teams/src/requests/poll.h
deleted file mode 100644
index b2573a43e2..0000000000
--- a/protocols/Teams/src/requests/poll.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright (c) 2015-25 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
-as published by the Free Software Foundation version 2
-of the License.
-
-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, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_POLL_H_
-#define _SKYPE_POLL_H_
-
-struct PollRequest : public AsyncHttpRequest
-{
- PollRequest(CTeamsProto *ppro) :
- AsyncHttpRequest(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(ppro->m_szId) + "/subscriptions/0/poll")
- {
- flags |= NLHRF_PERSISTENT;
- timeout = 120000;
-
- if (ppro->m_iPollingId != -1)
- m_szUrl.AppendFormat("?ackId=%d", ppro->m_iPollingId);
-
- AddHeader("Referer", "https://web.skype.com/main");
- AddHeader("ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=swx-skype.com; clientVer=908/1.85.0.29");
- AddHeader("Accept", "application/json");
- AddHeader("Accept-Language", "en, C");
- }
-};
-#endif //_SKYPE_POLL_H_ \ No newline at end of file
diff --git a/protocols/Teams/src/stdafx.cxx b/protocols/Teams/src/stdafx.cxx
index 4a1e1f0e70..b64bcca703 100644
--- a/protocols/Teams/src/stdafx.cxx
+++ b/protocols/Teams/src/stdafx.cxx
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2025 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/Teams/src/stdafx.h b/protocols/Teams/src/stdafx.h
index de676dace4..7cbbec2676 100644
--- a/protocols/Teams/src/stdafx.h
+++ b/protocols/Teams/src/stdafx.h
@@ -88,16 +88,11 @@ struct AsyncHttpRequest : public MTHttpRequest<CTeamsProto>
#include "teams_proto.h"
-#include "requests/capabilities.h"
#include "requests/chatrooms.h"
-#include "requests/contacts.h"
#include "requests/history.h"
-#include "requests/poll.h"
#include "requests/profile.h"
#include "requests/search.h"
#include "requests/status.h"
#include "requests/subscriptions.h"
-#define POLLING_ERRORS_LIMIT 3
-
-#endif //_COMMON_H_ \ No newline at end of file
+#endif //_COMMON_H_
diff --git a/protocols/Teams/src/teams_avatars.cpp b/protocols/Teams/src/teams_avatars.cpp
index 6bbac21d04..533aa62d08 100644
--- a/protocols/Teams/src/teams_avatars.cpp
+++ b/protocols/Teams/src/teams_avatars.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -191,7 +191,7 @@ struct SetAvatarRequest : public AsyncHttpRequest
void CTeamsProto::OnSentAvatar(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply root(response);
+ TeamsReply root(response);
if (root.error())
return;
}
diff --git a/protocols/Teams/src/teams_chatrooms.cpp b/protocols/Teams/src/teams_chatrooms.cpp
index 42c5f269ce..e4012e376f 100644
--- a/protocols/Teams/src/teams_chatrooms.cpp
+++ b/protocols/Teams/src/teams_chatrooms.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -353,7 +353,7 @@ void CTeamsProto::SendChatMessage(SESSION_INFO *si, const wchar_t *tszMessage)
void CTeamsProto::OnGetChatMembers(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -375,7 +375,7 @@ void CTeamsProto::OnGetChatMembers(MHttpResponse *response, AsyncHttpRequest *pR
void CTeamsProto::OnGetChatInfo(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
diff --git a/protocols/Teams/src/teams_contacts.cpp b/protocols/Teams/src/teams_contacts.cpp
index b8f94be72d..8579aed3c3 100644
--- a/protocols/Teams/src/teams_contacts.cpp
+++ b/protocols/Teams/src/teams_contacts.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -100,9 +100,11 @@ MCONTACT CTeamsProto::AddContact(const char *skypeId, const char *nick, bool isT
return hContact;
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::LoadContactsAuth(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -140,7 +142,7 @@ void CTeamsProto::LoadContactsAuth(MHttpResponse *response, AsyncHttpRequest*)
void CTeamsProto::LoadContactList(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -220,16 +222,14 @@ void CTeamsProto::LoadContactList(MHttpResponse *response, AsyncHttpRequest*)
}
}
- PushRequest(new GetContactsAuthRequest());
+ PushRequest(new AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/invites", &CTeamsProto::LoadContactsAuth));
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
INT_PTR CTeamsProto::OnRequestAuth(WPARAM hContact, LPARAM)
{
- if (hContact == INVALID_CONTACT_ID)
- return 1;
-
- PushRequest(new AddContactRequest(getId(hContact)));
- return 0;
+ return AuthRequest(hContact, 0);
}
INT_PTR CTeamsProto::OnGrantAuth(WPARAM hContact, LPARAM)
@@ -237,7 +237,7 @@ INT_PTR CTeamsProto::OnGrantAuth(WPARAM hContact, LPARAM)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthAcceptRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/accept"));
return 0;
}
@@ -257,15 +257,6 @@ bool CTeamsProto::OnContactDeleted(MCONTACT hContact, uint32_t flags)
/////////////////////////////////////////////////////////////////////////////////////////
-INT_PTR CTeamsProto::BlockContact(WPARAM hContact, LPARAM)
-{
- if (!IsOnline()) return 1;
-
- if (IDYES == MessageBox(NULL, TranslateT("Are you sure?"), TranslateT("Warning"), MB_YESNO | MB_ICONQUESTION))
- PushRequest(new BlockContactRequest(this, hContact));
- return 0;
-}
-
void CTeamsProto::OnBlockContact(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
MCONTACT hContact = (DWORD_PTR)pRequest->pUserInfo;
@@ -273,12 +264,21 @@ void CTeamsProto::OnBlockContact(MHttpResponse *response, AsyncHttpRequest *pReq
Contact::Hide(hContact);
}
-INT_PTR CTeamsProto::UnblockContact(WPARAM hContact, LPARAM)
+INT_PTR CTeamsProto::BlockContact(WPARAM hContact, LPARAM)
{
- PushRequest(new UnblockContactRequest(this, hContact));
+ if (!IsOnline()) return 1;
+
+ if (IDYES == MessageBox(NULL, TranslateT("Are you sure?"), TranslateT("Warning"), MB_YESNO | MB_ICONQUESTION)) {
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + mir_urlEncode(getId(hContact)), &CTeamsProto::OnBlockContact);
+ pReq->m_szParam = "{\"report_abuse\":\"false\",\"ui_version\":\"skype.com\"}";
+ pReq->pUserInfo = (void *)hContact;
+ PushRequest(pReq);
+ }
return 0;
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::OnUnblockContact(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
if (response == nullptr)
@@ -288,3 +288,14 @@ void CTeamsProto::OnUnblockContact(MHttpResponse *response, AsyncHttpRequest *pR
Contact::Hide(hContact, false);
delSetting(hContact, "IsBlocked");
}
+
+INT_PTR CTeamsProto::UnblockContact(WPARAM hContact, LPARAM)
+{
+ if (!IsOnline()) return 1;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_DELETE, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + mir_urlEncode(getId(hContact)), &CTeamsProto::OnUnblockContact);
+ pReq->pUserInfo = (void *)hContact;
+ pReq << CHAR_PARAM("reporterIp", "123.123.123.123") << CHAR_PARAM("uiVersion", g_szMirVer); // TODO: user ip address
+ PushRequest(pReq);
+ return 0;
+}
diff --git a/protocols/Teams/src/teams_endpoint.cpp b/protocols/Teams/src/teams_endpoint.cpp
index 39b6958abb..733008ee75 100644
--- a/protocols/Teams/src/teams_endpoint.cpp
+++ b/protocols/Teams/src/teams_endpoint.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -17,15 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-void CTeamsProto::ProcessTimer()
-{
- if (!IsOnline())
- return;
-
- PushRequest(new GetContactListRequest());
- SendPresence();
-}
-
void CTeamsProto::SendCreateEndpoint()
{
auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints", &CTeamsProto::OnEndpointCreated);
@@ -83,19 +74,17 @@ void CTeamsProto::OnEndpointCreated(MHttpResponse *response, AsyncHttpRequest*)
if (name == "registrationToken")
m_szToken = val.Detach();
else if (name == "endpointId")
- m_szId = val.Detach();
+ m_szEndpoint = val.Detach();
}
}
- if (m_szId && m_hPollingThread == nullptr)
- ForkThread(&CTeamsProto::PollingThread);
-
+ StartTrouter();
PushRequest(new CreateSubscriptionsRequest());
}
void CTeamsProto::OnEndpointDeleted(MHttpResponse *, AsyncHttpRequest *)
{
- m_szId = nullptr;
+ m_szEndpoint = nullptr;
m_szToken = nullptr;
}
@@ -111,23 +100,9 @@ void CTeamsProto::OnSubscriptionsCreated(MHttpResponse *response, AsyncHttpReque
SendPresence();
}
-void CTeamsProto::SendPresence()
-{
- ptrA epname;
-
- if (!m_bUseHostnameAsPlace && m_wstrPlace && *m_wstrPlace)
- epname = mir_utf8encodeW(m_wstrPlace);
- else {
- wchar_t compName[MAX_COMPUTERNAME_LENGTH + 1];
- DWORD size = _countof(compName);
- GetComputerName(compName, &size);
- epname = mir_utf8encodeW(compName);
- }
-
- PushRequest(new SendCapabilitiesRequest(epname, this));
-}
+/////////////////////////////////////////////////////////////////////////////////////////
-void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest*)
+void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest *)
{
if (response == nullptr || response->body.IsEmpty()) {
ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
@@ -147,7 +122,7 @@ void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest
skypenames.destroy();
ReceiveAvatar(0);
- PushRequest(new GetContactListRequest());
+ PushRequest(new AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/contacts", &CTeamsProto::LoadContactList));
PushRequest(new SyncConversations());
JSONNode root = JSONNode::parse(response->body);
@@ -157,6 +132,38 @@ void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest
PushRequest(new GetProfileRequest(this, 0));
}
+void CTeamsProto::SendPresence()
+{
+ ptrA epname;
+
+ if (!m_bUseHostnameAsPlace && m_wstrPlace && *m_wstrPlace)
+ epname = mir_utf8encodeW(m_wstrPlace);
+ else {
+ wchar_t compName[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD size = _countof(compName);
+ GetComputerName(compName, &size);
+ epname = mir_utf8encodeW(compName);
+ }
+
+ JSONNode privateInfo; privateInfo.set_name("privateInfo");
+ privateInfo << CHAR_PARAM("epname", epname);
+
+ JSONNode publicInfo; publicInfo.set_name("publicInfo");
+ publicInfo << CHAR_PARAM("capabilities", "Audio|Video") << INT_PARAM("typ", 125)
+ << CHAR_PARAM("skypeNameVersion", "Miranda NG Skype") << CHAR_PARAM("nodeInfo", "xx") << CHAR_PARAM("version", g_szMirVer);
+
+ JSONNode node;
+ node << CHAR_PARAM("id", "messagingService") << CHAR_PARAM("type", "EndpointPresenceDoc")
+ << CHAR_PARAM("selfLink", "uri") << privateInfo << publicInfo;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(m_szEndpoint) + "/presenceDocs/messagingService",
+ &CTeamsProto::OnCapabilitiesSended);
+ pReq->m_szParam = node.write().c_str();
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::OnStatusChanged(MHttpResponse *response, AsyncHttpRequest*)
{
if (response == nullptr || response->body.IsEmpty()) {
diff --git a/protocols/Teams/src/teams_files.cpp b/protocols/Teams/src/teams_files.cpp
index a6e7d07087..4efb89a662 100644
--- a/protocols/Teams/src/teams_files.cpp
+++ b/protocols/Teams/src/teams_files.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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
+as published by the Free Software Foundation version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
////////////////////////////////////////////////////////////////////////////////////////
@@ -48,7 +65,7 @@ void CTeamsProto::ReceiveFileThread(void *param)
nlhr.AddHeader("Cookie", szCookie);
NLHR_PTR response(Netlib_HttpTransaction(m_hNetlibUser, &nlhr));
if (response) {
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (!reply.error()) {
auto &root = reply.data();
if (root["content_state"].as_string() == "ready")
diff --git a/protocols/Teams/src/teams_history.cpp b/protocols/Teams/src/teams_history.cpp
index eb6a9ca39f..a2502a0eb3 100644
--- a/protocols/Teams/src/teams_history.cpp
+++ b/protocols/Teams/src/teams_history.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
void CTeamsProto::OnGetServerHistory(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -96,7 +96,7 @@ INT_PTR CTeamsProto::SvcLoadHistory(WPARAM hContact, LPARAM)
void CTeamsProto::OnSyncConversations(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
diff --git a/protocols/Teams/src/teams_http.cpp b/protocols/Teams/src/teams_http.cpp
index 18e3067119..a24b44bfbc 100644
--- a/protocols/Teams/src/teams_http.cpp
+++ b/protocols/Teams/src/teams_http.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -23,7 +23,7 @@ AsyncHttpRequest::AsyncHttpRequest(int type, SkypeHost host, LPCSTR url, MTHttpR
switch (host) {
case HOST_API: m_szUrl = "api.skype.com"; break;
case HOST_PEOPLE: m_szUrl = "people.skype.com/v2"; break;
- case HOST_CONTACTS: m_szUrl = "edge.skype.com/pcs/contacts/v2"; break;
+ case HOST_CONTACTS: m_szUrl = "contacts.skype.com/contacts/v2"; break;
case HOST_GRAPH: m_szUrl = "skypegraph.skype.com"; break;
case HOST_LOGIN: m_szUrl = "login.microsoftonline.com"; break;
case HOST_TEAMS: m_szUrl = "teams.live.com"; break;
diff --git a/protocols/Teams/src/teams_login.cpp b/protocols/Teams/src/teams_login.cpp
index 9e4f2a966a..b3c800d13a 100644
--- a/protocols/Teams/src/teams_login.cpp
+++ b/protocols/Teams/src/teams_login.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -40,8 +40,6 @@ void CTeamsProto::LoggedIn()
m_iStatus = m_iDesiredStatus;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
- m_impl.m_heartBeat.StartSafe(600 * 1000);
-
SendCreateEndpoint();
}
diff --git a/protocols/Teams/src/teams_menus.cpp b/protocols/Teams/src/teams_menus.cpp
index 9238c33946..8605e30a52 100644
--- a/protocols/Teams/src/teams_menus.cpp
+++ b/protocols/Teams/src/teams_menus.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_mood.cpp b/protocols/Teams/src/teams_mood.cpp
index 66a2b15444..fb366ac97a 100644
--- a/protocols/Teams/src/teams_mood.cpp
+++ b/protocols/Teams/src/teams_mood.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_options.cpp b/protocols/Teams/src/teams_options.cpp
index 040583ad85..693af22fb1 100644
--- a/protocols/Teams/src/teams_options.cpp
+++ b/protocols/Teams/src/teams_options.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_polling.cpp b/protocols/Teams/src/teams_polling.cpp
index ca59ac8f6a..11a918034e 100644
--- a/protocols/Teams/src/teams_polling.cpp
+++ b/protocols/Teams/src/teams_polling.cpp
@@ -17,42 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-void CTeamsProto::PollingThread(void *)
-{
- debugLogA(__FUNCTION__ ": entering");
-
- m_iPollingId = -1;
-
- while (true) {
- if (m_isTerminated || m_szId == nullptr)
- break;
-
- std::unique_ptr<PollRequest> request(new PollRequest(this));
- request->nlc = m_hPollingConn;
- NLHR_PTR response(DoSend(request.get()));
- if (m_isTerminated || m_szId == nullptr)
- break;
-
- if (response == nullptr || response->resultCode != 200) {
- m_hPollingConn = nullptr;
- continue;
- }
-
- m_hPollingConn = response->nlc;
- if (!response->body.IsEmpty())
- ParsePollData(response->body);
- }
-
- if (!m_isTerminated) {
- debugLogA(__FUNCTION__ ": unexpected termination; switching protocol to offline");
- SetStatus(ID_STATUS_OFFLINE);
- }
-
- m_hPollingConn = nullptr;
- m_hPollingThread = nullptr;
- debugLogA(__FUNCTION__ ": leaving");
-}
-
void CTeamsProto::ParsePollData(const char *szData)
{
debugLogA(__FUNCTION__);
@@ -171,6 +135,3 @@ void CTeamsProto::ProcessUserPresence(const JSONNode &node)
}
}
}
-
-void CTeamsProto::ProcessConversationUpdate(const JSONNode &) {}
-void CTeamsProto::ProcessThreadUpdate(const JSONNode &) {}
diff --git a/protocols/Teams/src/teams_popups.cpp b/protocols/Teams/src/teams_popups.cpp
index efac8c26f9..59cca9e937 100644
--- a/protocols/Teams/src/teams_popups.cpp
+++ b/protocols/Teams/src/teams_popups.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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
+as published by the Free Software Foundation version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
void CTeamsProto::InitPopups()
diff --git a/protocols/Teams/src/teams_profile.cpp b/protocols/Teams/src/teams_profile.cpp
index a26d88b1fe..29a04937c6 100644
--- a/protocols/Teams/src/teams_profile.cpp
+++ b/protocols/Teams/src/teams_profile.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -105,7 +105,7 @@ void CTeamsProto::LoadProfile(MHttpResponse *response, AsyncHttpRequest *pReques
{
MCONTACT hContact = (DWORD_PTR)pRequest->pUserInfo;
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error()) {
ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, 0);
return;
diff --git a/protocols/Teams/src/teams_proto.cpp b/protocols/Teams/src/teams_proto.cpp
index 28dcf637ad..9999e41c2e 100644
--- a/protocols/Teams/src/teams_proto.cpp
+++ b/protocols/Teams/src/teams_proto.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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
+as published by the Free Software Foundation version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
@@ -22,6 +39,13 @@ CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
nlu.szSettingsModule = m_szModuleName;
m_hNetlibUser = Netlib_RegisterUser(&nlu);
+ CMStringA module(FORMAT, "%s.TRouter", m_szModuleName);
+ CMStringW descr(FORMAT, TranslateT("%s websocket connection"), m_tszUserName);
+ nlu.szSettingsModule = module.GetBuffer();
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_UNICODE;
+ nlu.szDescriptiveName.w = descr.GetBuffer();
+ m_hTrouterNetlibUser = Netlib_RegisterUser(&nlu);
+
CreateProtoService(PS_GETAVATARINFO, &CTeamsProto::SvcGetAvatarInfo);
CreateProtoService(PS_GETAVATARCAPS, &CTeamsProto::SvcGetAvatarCaps);
CreateProtoService(PS_GETMYAVATAR, &CTeamsProto::SvcGetMyAvatar);
@@ -83,6 +107,7 @@ void CTeamsProto::OnModulesLoaded()
void CTeamsProto::OnShutdown()
{
StopQueue();
+ StopTrouter();
}
INT_PTR CTeamsProto::GetCaps(int type, MCONTACT)
@@ -142,7 +167,7 @@ int CTeamsProto::Authorize(MEVENT hDbEvent)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthAcceptRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/accept"));
return 0;
}
@@ -152,7 +177,7 @@ int CTeamsProto::AuthDeny(MEVENT hDbEvent, const wchar_t *)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthDeclineRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/decline"));
return 0;
}
@@ -166,7 +191,15 @@ int CTeamsProto::AuthRequest(MCONTACT hContact, const wchar_t *szMessage)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AddContactRequest(getId(hContact), T2Utf(szMessage)));
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts");
+
+ JSONNode node;
+ node << CHAR_PARAM("mri", getId(hContact));
+ if (mir_wstrlen(szMessage))
+ node << WCHAR_PARAM("greeting", szMessage);
+ pReq->m_szParam = node.write().c_str();
+
+ PushRequest(pReq);
return 0;
}
@@ -203,6 +236,7 @@ int CTeamsProto::SetStatus(int iNewStatus)
if (iNewStatus == ID_STATUS_OFFLINE) {
m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
StopQueue();
+ StopTrouter();
ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, ID_STATUS_OFFLINE);
diff --git a/protocols/Teams/src/teams_proto.h b/protocols/Teams/src/teams_proto.h
index 850aae8808..c6ad876e86 100644
--- a/protocols/Teams/src/teams_proto.h
+++ b/protocols/Teams/src/teams_proto.h
@@ -1,4 +1,8 @@
#define TEAMS_CLIENT_ID "8ec6bc83-69c8-4392-8f08-b3c986009232"
+#define TEAMS_CLIENTINFO_NAME "skypeteams"
+#define TEAMS_CLIENTINFO_VERSION "49/24062722442"
+
+#define TEAMS_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0 Teams/24165.1410.2974.6689/49"
#define DBKEY_ID "id"
#define DBKEY_GROUP "DefaultGroup"
@@ -39,7 +43,7 @@ class CTeamsProto : public PROTO<CTeamsProto>
CTimer m_heartBeat, m_loginPoll;
void OnHeartBeat(CTimer *)
{
- m_proto.ProcessTimer();
+ m_proto.TRouterSendJson("ping");
}
void OnLoginPoll(CTimer *)
{
@@ -141,12 +145,6 @@ public:
void __cdecl SearchBasicThread(void *param);
//////////////////////////////////////////////////////////////////////////////////////
- // services
-
- static INT_PTR __cdecl SvcEventGetIcon(WPARAM, LPARAM);
- static INT_PTR __cdecl SvcGetEventText(WPARAM, LPARAM);
-
- //////////////////////////////////////////////////////////////////////////////////////
// settings
CMOption<bool> m_bAutoHistorySync;
@@ -164,7 +162,7 @@ public:
// other data
int m_iPollingId, m_iMessageId = 1;
- ptrA m_szToken, m_szId, m_szOwnSkypeId;
+ ptrA m_szToken, m_szEndpoint, m_szOwnSkypeId;
CMStringA m_szSkypename, m_szMyname, m_szSkypeToken;
MCONTACT m_hMyContact;
@@ -182,12 +180,6 @@ public:
void OnEndpointCreated(MHttpResponse *response, AsyncHttpRequest *pRequest);
void OnEndpointDeleted(MHttpResponse *response, AsyncHttpRequest *pRequest);
- // oauth
- void OnOAuthStart(MHttpResponse *response, AsyncHttpRequest *pRequest);
- void OnOAuthConfirm(MHttpResponse* response, AsyncHttpRequest* pRequest);
- void OnOAuthAuthorize(MHttpResponse* response, AsyncHttpRequest* pRequest);
- void OnOAuthEnd(MHttpResponse *response, AsyncHttpRequest *pRequest);
-
void OnASMObjectCreated(MHttpResponse *response, AsyncHttpRequest *pRequest);
void OnASMObjectUploaded(MHttpResponse *response, AsyncHttpRequest *pRequest);
@@ -211,6 +203,7 @@ public:
void LoadProfile(MHttpResponse *response, AsyncHttpRequest *pRequest);
static INT_PTR __cdecl GlobalParseSkypeUriService(WPARAM, LPARAM lParam);
+
private:
bool m_bHistorySynced;
@@ -224,9 +217,6 @@ private:
mir_cs messageSyncLock;
mir_cs m_StatusLock;
- HANDLE m_hPollingThread;
- HNETLIBCONN m_hPollingConn;
-
// avatars
void SetAvatarUrl(MCONTACT hContact, const CMStringW &tszUrl);
bool ReceiveAvatar(MCONTACT hContact);
@@ -303,17 +293,7 @@ private:
void SetChatStatus(MCONTACT hContact, int iStatus);
- // polling
- void __cdecl PollingThread(void*);
-
bool ParseMessage(const JSONNode &node, DB::EventInfo &dbei);
- void ParsePollData(const char*);
-
- void ProcessNewMessage(const JSONNode &node);
- void ProcessUserPresence(const JSONNode &node);
- void ProcessThreadUpdate(const JSONNode &node);
- void ProcessEndpointPresence(const JSONNode &node);
- void ProcessConversationUpdate(const JSONNode &node);
// utils
template <typename T>
@@ -346,8 +326,6 @@ private:
static LRESULT CALLBACK PopupDlgProcCall(HWND hPopup, UINT uMsg, WPARAM wParam, LPARAM lParam);
- void ProcessTimer();
-
void SetString(MCONTACT hContact, const char *pszSetting, const JSONNode &node);
CMStringW ChangeTopicForm();
@@ -369,6 +347,40 @@ private:
auto *proto = CMPlugin::getInstance((MCONTACT)wParam);
return proto ? (proto->*Service)(wParam, lParam) : 0;
}
+
+ // trouter
+public:
+ void TRouterProcess(const char *str);
+
+private:
+ HNETLIBUSER m_hTrouterNetlibUser;
+ CMStringA m_szTrouterUrl, m_szTrouterSurl;
+ WebSocket<CTeamsProto> *m_ws;
+ MHttpHeaders m_connectParams;
+ int iCommandId;
+
+ void ProcessNewMessage(const JSONNode &node);
+ void ProcessUserPresence(const JSONNode &node);
+ void ProcessThreadUpdate(const JSONNode &node);
+ void ProcessServerMessage(const std::string &szName, const JSONNode &args);
+ void ProcessEndpointPresence(const JSONNode &node);
+ void ProcessConversationUpdate(const JSONNode &node);
+
+ void __cdecl GatewayThread(void *);
+
+ void TRouterSendJson(const char *szName, const JSONNode *node = 0);
+ void TRouterSendJson(const JSONNode &node);
+
+ void TRouterSendAuthentication();
+ void TRouterSendActive(bool);
+ void TRouterRegister();
+ void TRouterRegister(const char *pszAppId, const char *pszKey, const char *pszPath);
+
+ void StartTrouter();
+ void StopTrouter();
+
+ void OnTrouterInfo(MHttpResponse *response, AsyncHttpRequest *pRequest);
+ void OnTrouterSession(MHttpResponse *response, AsyncHttpRequest *pRequest);
};
typedef CProtoDlgBase<CTeamsProto> CTeamsDlgBase;
diff --git a/protocols/Teams/src/teams_search.cpp b/protocols/Teams/src/teams_search.cpp
index a88daaae47..5cceab3c3f 100644
--- a/protocols/Teams/src/teams_search.cpp
+++ b/protocols/Teams/src/teams_search.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -34,7 +34,7 @@ void CTeamsProto::OnSearch(MHttpResponse *response, AsyncHttpRequest*)
{
debugLogA(__FUNCTION__);
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error()) {
ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0);
return;
diff --git a/protocols/Teams/src/teams_trouter.cpp b/protocols/Teams/src/teams_trouter.cpp
new file mode 100644
index 0000000000..e602724c68
--- /dev/null
+++ b/protocols/Teams/src/teams_trouter.cpp
@@ -0,0 +1,367 @@
+/*
+Copyright (C) 2025 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
+as published by the Free Software Foundation version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define TEAMS_TROUTER_TTL 86400
+#define TEAMS_TROUTER_TCCV "2024.23.01.2"
+
+void CTeamsProto::OnTrouterSession(MHttpResponse *response, AsyncHttpRequest *pRequest)
+{
+ if (response->resultCode != 200) {
+ LoginError();
+ return;
+ }
+
+ int iStart = 0;
+ CMStringA szId = response->body.Tokenize(":", iStart);
+ m_szTrouterUrl = pRequest->m_szUrl;
+ m_szTrouterUrl.Replace("socket.io/1/", "socket.io/1/websocket/" + szId + "/");
+ ForkThread(&CTeamsProto::GatewayThread);
+}
+
+void CTeamsProto::OnTrouterInfo(MHttpResponse *response, AsyncHttpRequest *)
+{
+ TeamsReply reply(response);
+ if (reply.error()) {
+ LoginError();
+ return;
+ }
+
+ auto &root = reply.data();
+ m_szTrouterSurl = root["surl"].as_mstring();
+ CMStringA ccid = root["ccid"].as_mstring();
+ CMStringA szUrl = root["socketio"].as_mstring();
+ szUrl += "socket.io/1/";
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_GET, HOST_OTHER, szUrl, &CTeamsProto::OnTrouterSession);
+ pReq << CHAR_PARAM("v", "v4");
+
+ m_connectParams.destroy();
+ for (auto &it : root["connectparams"]) {
+ m_connectParams.AddHeader(it.name(), it.as_string().c_str());
+ pReq << CHAR_PARAM(it.name(), it.as_string().c_str());
+ }
+
+ pReq << CHAR_PARAM("tc", "{\"cv\":\"" TEAMS_TROUTER_TCCV "\",\"ua\":\"TeamsCDL\",\"hr\":\"\",\"v\":\"" TEAMS_CLIENTINFO_VERSION "\"}")
+ << CHAR_PARAM("con_num", "1234567890123_1") << CHAR_PARAM("epid", m_szEndpoint) << BOOL_PARAM("auth", true) << INT_PARAM("timeout", 40);
+ if (!ccid.IsEmpty())
+ pReq << CHAR_PARAM("ccid", ccid);
+ PushRequest(pReq);
+}
+
+void CTeamsProto::StartTrouter()
+{
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://go.trouter.teams.microsoft.com/v4/a", &CTeamsProto::OnTrouterInfo);
+ pReq->m_szUrl.AppendFormat("?epid=%s", m_szEndpoint.get());
+ pReq->AddHeader("x-skypetoken", m_szSkypeToken);
+ pReq->flags |= NLHRF_NODUMPHEADERS;
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTeamsProto::StopTrouter()
+{
+ m_impl.m_heartBeat.StopSafe();
+
+ if (m_ws) {
+ m_ws->terminate();
+ m_ws = nullptr;
+ }
+}
+
+void CTeamsProto::GatewayThread(void *)
+{
+ m_ws = nullptr;
+
+ MHttpHeaders headers;
+ headers.AddHeader("x-skypetoken", m_szSkypeToken);
+ headers.AddHeader("User-Agent", TEAMS_USER_AGENT);
+
+ WebSocket<CTeamsProto> ws(this);
+ NLHR_PTR pReply(ws.connect(m_hTrouterNetlibUser, m_szTrouterUrl, &headers));
+ if (pReply) {
+ if (pReply->resultCode == 101) {
+ m_ws = &ws;
+
+ iCommandId = 1;
+ m_impl.m_heartBeat.StartSafe(30000);
+
+ debugLogA("Websocket connection succeeded");
+ ws.run();
+ }
+ else debugLogA("websocket connection failed: %d", pReply->resultCode);
+ }
+ else debugLogA("websocket connection failed");
+
+ StopTrouter();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// TRouter send
+
+void CTeamsProto::TRouterSendJson(const JSONNode &node)
+{
+ std::string szJson = "5:::" + node.write();
+ if (m_ws) {
+ m_ws->sendText(szJson.c_str());
+ }
+}
+
+void CTeamsProto::TRouterSendJson(const char *szName, const JSONNode *node)
+{
+ JSONNode payload, args(JSON_ARRAY);
+ payload << CHAR_PARAM("name", szName);
+ if (node) {
+ args.set_name("args");
+ args << *node;
+ payload << args;
+ }
+
+ std::string szJson = payload.write();
+ if (m_ws)
+ m_ws->sendText(szJson.c_str());
+}
+
+void CTeamsProto::TRouterSendAuthentication()
+{
+ JSONNode headers, params, payload;
+
+ headers.set_name("headers");
+ headers << CHAR_PARAM("X-Ms-Test-User", "False") << CHAR_PARAM("Authorization", "Bearer " + m_szAccessToken)
+ << CHAR_PARAM("X-MS-Migration", "True");
+
+ params.set_name("connectparams");
+ for (auto &it : m_connectParams)
+ params << CHAR_PARAM(it->szName, it->szValue);
+
+ payload << headers << params;
+ TRouterSendJson(payload);
+}
+
+static char szSuffix[4] = { 'A', 'g', 'Q', 'w' };
+
+void CTeamsProto::TRouterSendActive(bool bActive)
+{
+ CMStringA cv;
+ srand(time(0));
+ for (int i = 0; i < 21; i++)
+ cv.AppendChar('a' + rand() % 26);
+ cv.AppendChar(szSuffix[rand() % 4]);
+ cv += ".0.1";
+
+ JSONNode payload;
+ payload << CHAR_PARAM("state", bActive ? "active" : "inactive") << CHAR_PARAM("cv", cv);
+ TRouterSendJson("user.activity", &payload);
+}
+
+void CTeamsProto::TRouterRegister()
+{
+ TRouterRegister("NextGenCalling", "DesktopNgc_2.3:SkypeNgc", m_szTrouterSurl + "NGCallManagerWin");
+ TRouterRegister("SkypeSpacesWeb", "SkypeSpacesWeb_2.3", m_szTrouterSurl + "SkypeSpacesWeb");
+ TRouterRegister("TeamsCDLWebWorker", "TeamsCDLWebWorker_1.9", m_szTrouterSurl);
+}
+
+void CTeamsProto::TRouterRegister(const char *pszAppId, const char *pszKey, const char *pszPath)
+{
+ JSONNode descr, reg, obj, trouter(JSON_ARRAY), transports;
+ descr.set_name("clientDescription");
+ descr << CHAR_PARAM("appId", pszAppId) << CHAR_PARAM("aesKey", "") << CHAR_PARAM("languageId", "en-US")
+ << CHAR_PARAM("platform", "edge") << CHAR_PARAM("templateKey", pszKey) << CHAR_PARAM("platformUIVersion", TEAMS_CLIENTINFO_VERSION)
+ << CHAR_PARAM("productContext", "TFL");
+
+ obj << CHAR_PARAM("context", "") << CHAR_PARAM("path", pszPath) << INT_PARAM("ttl", TEAMS_TROUTER_TTL);
+ trouter.set_name("TROUTER"); trouter << obj;
+ transports.set_name("transports"); transports << trouter;
+
+ reg.set_name("registration");
+ reg << descr << CHAR_PARAM("registrationId", m_szEndpoint) << CHAR_PARAM("nodeId", "") << transports;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://edge.skype.com/registrar/prod/v2/registrations");
+ pReq->flags |= NLHRF_NODUMPHEADERS;
+ pReq->AddHeader("Content-Type", "application/json");
+ pReq->AddHeader("X-Skypetoken", m_szSkypeToken);
+ pReq->AddHeader("Authorization", "Bearer " + m_szAccessToken);
+ pReq->m_szParam = reg.write().c_str();
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// TRouter receive
+
+void WebSocket<CTeamsProto>::process(const uint8_t *buf, size_t cbLen)
+{
+ Netlib_Dump(getConn(), buf, cbLen, false, 0);
+
+ CMStringA payload((const char *)buf, (int)cbLen);
+ p->TRouterProcess(payload);
+}
+
+static const char* skip3colons(const char *str)
+{
+ int nColons = 3;
+ for (const char *p = str; *p; p++) {
+ if (*p == ':') {
+ if (--nColons == 0)
+ return p + 1;
+ }
+ }
+ return str;
+}
+
+void CTeamsProto::TRouterProcess(const char *str)
+{
+ switch (*str) {
+ case '1':
+ TRouterSendAuthentication();
+ TRouterSendActive(true);
+ TRouterRegister();
+ break;
+
+ case '3':
+ if (auto packet = JSONNode::parse(skip3colons(str))) {
+ std::string szBody(packet["body"].as_string());
+ auto message = JSONNode::parse(szBody.c_str());
+ if (message) {
+ Netlib_Logf(m_hTrouterNetlibUser, "Got event:\n%s", message.write_formatted().c_str());
+
+ const JSONNode &resource = message["resource"];
+
+ std::string resourceType = message["resourceType"].as_string();
+ if (resourceType == "NewMessage")
+ ProcessNewMessage(resource);
+ else if (resourceType == "UserPresence")
+ ProcessUserPresence(resource);
+ else if (resourceType == "EndpointPresence")
+ ProcessEndpointPresence(resource);
+ else if (resourceType == "ConversationUpdate")
+ ProcessConversationUpdate(resource);
+ else if (resourceType == "ThreadUpdate")
+ ProcessThreadUpdate(resource);
+ }
+ }
+ break;
+
+ case '5':
+ if (auto root = JSONNode::parse(skip3colons(str))) {
+ std::string szName(root["name"].as_string());
+ ProcessServerMessage(szName, root["args"]);
+ }
+ break;
+ }
+}
+
+void CTeamsProto::ProcessEndpointPresence(const JSONNode &node)
+{
+ debugLogA(__FUNCTION__);
+ std::string selfLink = node["selfLink"].as_string();
+ CMStringA skypename(UrlToSkypeId(selfLink.c_str()));
+
+ MCONTACT hContact = FindContact(skypename);
+ if (hContact == NULL)
+ return;
+
+ const JSONNode &publicInfo = node["publicInfo"];
+ const JSONNode &privateInfo = node["privateInfo"];
+ CMStringA MirVer;
+ if (publicInfo) {
+ std::string skypeNameVersion = publicInfo["skypeNameVersion"].as_string();
+ std::string version = publicInfo["version"].as_string();
+ std::string typ = publicInfo["typ"].as_string();
+ int iTyp = atoi(typ.c_str());
+ switch (iTyp) {
+ case 0:
+ case 1:
+ MirVer.Append("Skype (Web) " + ParseUrl(version.c_str(), "/"));
+ break;
+ case 10:
+ MirVer.Append("Skype (XBOX) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 17:
+ MirVer.Append("Skype (Android) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 16:
+ MirVer.Append("Skype (iOS) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 12:
+ MirVer.Append("Skype (WinRT) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 15:
+ MirVer.Append("Skype (WP) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 13:
+ MirVer.Append("Skype (OSX) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 11:
+ MirVer.Append("Skype (Windows) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 14:
+ MirVer.Append("Skype (Linux) " + ParseUrl(skypeNameVersion.c_str(), "/"));
+ break;
+ case 125:
+ MirVer.AppendFormat("Miranda NG Skype %s", version.c_str());
+ break;
+ default:
+ MirVer.Append("Skype (Unknown)");
+ }
+ }
+
+ if (privateInfo != NULL) {
+ std::string epname = privateInfo["epname"].as_string();
+ if (!epname.empty())
+ MirVer.AppendFormat(" [%s]", epname.c_str());
+ }
+
+ setString(hContact, "MirVer", MirVer);
+}
+
+void CTeamsProto::ProcessUserPresence(const JSONNode &node)
+{
+ debugLogA(__FUNCTION__);
+
+ std::string selfLink = node["selfLink"].as_string();
+ std::string status = node["availability"].as_string();
+ CMStringA skypename = UrlToSkypeId(selfLink.c_str());
+
+ if (!skypename.IsEmpty()) {
+ if (IsMe(skypename)) {
+ int iNewStatus = SkypeToMirandaStatus(status.c_str());
+ if (iNewStatus == ID_STATUS_OFFLINE) return;
+ int old_status = m_iStatus;
+ m_iDesiredStatus = iNewStatus;
+ m_iStatus = iNewStatus;
+ if (old_status != iNewStatus)
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, iNewStatus);
+ }
+ else {
+ MCONTACT hContact = FindContact(skypename);
+ if (hContact != NULL)
+ SetContactStatus(hContact, SkypeToMirandaStatus(status.c_str()));
+ }
+ }
+}
+
+void CTeamsProto::ProcessServerMessage(const std::string &szName, const JSONNode&)
+{
+ if (szName == "trouter.message_loss") {
+ TRouterRegister("TeamsCDLWebWorker", "TeamsCDLWebWorker_1.9", m_szTrouterSurl);
+ }
+}
+
+void CTeamsProto::ProcessConversationUpdate(const JSONNode &) {}
+void CTeamsProto::ProcessThreadUpdate(const JSONNode &) {}
diff --git a/protocols/Teams/src/teams_utils.cpp b/protocols/Teams/src/teams_utils.cpp
index 59beeaa894..3d23351468 100644
--- a/protocols/Teams/src/teams_utils.cpp
+++ b/protocols/Teams/src/teams_utils.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_utils.h b/protocols/Teams/src/teams_utils.h
index f7d205da61..58255e53c1 100644
--- a/protocols/Teams/src/teams_utils.h
+++ b/protocols/Teams/src/teams_utils.h
@@ -60,9 +60,9 @@ struct CFileUploadParam : public MZeroedObject
}
};
-struct SkypeReply : public JsonReply
+struct TeamsReply : public JsonReply
{
- SkypeReply(MHttpResponse *response) :
+ TeamsReply(MHttpResponse *response) :
JsonReply(response)
{
if (m_root)
diff --git a/protocols/Teams/src/version.h b/protocols/Teams/src/version.h
index 711a86277e..3b23908bca 100644
--- a/protocols/Teams/src/version.h
+++ b/protocols/Teams/src/version.h
@@ -7,7 +7,7 @@
#define __PLUGIN_NAME "Teams protocol"
#define __FILENAME "Teams.dll"
-#define __DESCRIPTION "Teams protocol support for Miranda NG."
+#define __DESCRIPTION "Microsoft Teams protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Teams"
#define __COPYRIGHT "© 2025 Miranda NG team"
diff --git a/protocols/Weather/res/resource.rc b/protocols/Weather/res/resource.rc
index b03db42abc..8c6e6227d8 100644
--- a/protocols/Weather/res/resource.rc
+++ b/protocols/Weather/res/resource.rc
@@ -182,7 +182,8 @@ BEGIN
CONTROL "History Log",IDC_TM7,"MButtonClass",WS_TABSTOP,2,221,77,9,WS_EX_WINDOWEDGE | WS_EX_NOACTIVATE | 0x10000000L
EDITTEXT IDC_HTEXT,80,219,183,12,ES_AUTOHSCROLL
GROUPBOX "Variable List",IDC_STATIC,207,14,99,191
- LTEXT "",IDC_VARLIST,213,25,86,157
+ CONTROL "",IDC_VARLIST,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,213,25,86,157
+ CONTROL "More Variables",IDC_MORE,"MButtonClass",WS_TABSTOP,216,187,81,15,WS_EX_WINDOWEDGE | WS_EX_NOACTIVATE | 0x10000000L
CONTROL "Reset",IDC_RESET,"MButtonClass",WS_TABSTOP,266,208,40,21,WS_EX_WINDOWEDGE | WS_EX_NOACTIVATE | 0x10000000L
EDITTEXT IDC_BTITLE2,80,28,125,12,ES_AUTOHSCROLL
CONTROL "Status Message",IDC_TM8,"MButtonClass",WS_TABSTOP | 0x100,2,29,77,9,WS_EX_WINDOWEDGE | WS_EX_NOACTIVATE | 0x10000000L
diff --git a/protocols/Weather/src/proto.h b/protocols/Weather/src/proto.h
index e83fee46eb..e1115b65e3 100644
--- a/protocols/Weather/src/proto.h
+++ b/protocols/Weather/src/proto.h
@@ -151,7 +151,6 @@ class CWeatherProto : public PROTO<CWeatherProto>
// data
void ConvertDataValue(WIDATAITEM *UpdateData);
void EraseAllInfo(void);
- void GetStationID(MCONTACT hContact, wchar_t *id, int idlen);
WEATHERINFO LoadWeatherInfo(MCONTACT hContact);
MHttpResponse* RunQuery(const wchar_t *id, int days);
@@ -182,6 +181,7 @@ class CWeatherProto : public PROTO<CWeatherProto>
int __cdecl OptInit(WPARAM, LPARAM);
CMStringW GetTextValue(int c);
+ void GetVarsDescr(CMStringW &str);
// popups
int WPShowMessage(const wchar_t *lpzText, int kind);
diff --git a/protocols/Weather/src/resource.h b/protocols/Weather/src/resource.h
index e53877d0ee..809c94857b 100644
--- a/protocols/Weather/src/resource.h
+++ b/protocols/Weather/src/resource.h
@@ -109,6 +109,7 @@
#define IDC_INFO10 2091
#define IDC_INFO12 2092
#define IDC_INFO13 2093
+#define IDC_MORE 2094
#define IDC_MOREDETAIL 2095
#define IDC_DATALIST 2096
#define IDC_MUPDATE 2097
diff --git a/protocols/Weather/src/stdafx.h b/protocols/Weather/src/stdafx.h
index eec03bd13e..cf1f3240a3 100644
--- a/protocols/Weather/src/stdafx.h
+++ b/protocols/Weather/src/stdafx.h
@@ -146,7 +146,7 @@ void TrimString(wchar_t *str);
void ConvertBackslashes(char *str);
char *GetSearchStr(char *dis);
-wchar_t *GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t* str);
+CMStringW GetDisplay(WEATHERINFO *w, const wchar_t *dis);
wchar_t *GetError(int code);
diff --git a/protocols/Weather/src/weather_addstn.cpp b/protocols/Weather/src/weather_addstn.cpp
index 1bdfb9b49d..026b9f8c67 100644
--- a/protocols/Weather/src/weather_addstn.cpp
+++ b/protocols/Weather/src/weather_addstn.cpp
@@ -88,7 +88,7 @@ MCONTACT CWeatherProto::AddToList(int, PROTOSEARCHRESULT *psr)
// if no default station is found, set the new contact as default station
if (opt.Default[0] == 0) {
- GetStationID(hContact, opt.Default, _countof(opt.Default));
+ wcsncpy_s(opt.Default, getMStringW(hContact, "ID"), _countof(opt.Default));
opt.DefStn = hContact;
ptrW wszNick(getWStringA(hContact, "Nick"));
diff --git a/protocols/Weather/src/weather_contacts.cpp b/protocols/Weather/src/weather_contacts.cpp
index 2661c1b20e..2f5a08f4ef 100644
--- a/protocols/Weather/src/weather_contacts.cpp
+++ b/protocols/Weather/src/weather_contacts.cpp
@@ -53,13 +53,12 @@ INT_PTR CWeatherProto::ViewLog(WPARAM wParam, LPARAM lParam)
// read complete forecast
// wParam = current contact
-INT_PTR CWeatherProto::LoadForecast(WPARAM wParam, LPARAM)
+INT_PTR CWeatherProto::LoadForecast(WPARAM hContact, LPARAM)
{
- wchar_t id[256];
- GetStationID(wParam, id, _countof(id));
- if (id[0] != 0) {
+ CMStringW wszID(getMStringW(hContact, "ID"));
+ if (!wszID.IsEmpty()) {
// set the url and open the webpage
- CMStringA szUrl("https://www.visualcrossing.com/weather-forecast/" + mir_urlEncode(T2Utf(id)) + "/metric");
+ CMStringA szUrl("https://www.visualcrossing.com/weather-forecast/" + mir_urlEncode(T2Utf(wszID)) + "/metric");
Utils_OpenUrl(szUrl);
}
return 0;
@@ -69,13 +68,12 @@ INT_PTR CWeatherProto::LoadForecast(WPARAM wParam, LPARAM)
// load weather map
// wParam = current contact
-INT_PTR CWeatherProto::WeatherMap(WPARAM wParam, LPARAM)
+INT_PTR CWeatherProto::WeatherMap(WPARAM hContact, LPARAM)
{
- wchar_t id[256];
- GetStationID(wParam, id, _countof(id));
- if (id[0] != 0) {
+ CMStringW wszID(getMStringW(hContact, "ID"));
+ if (!wszID.IsEmpty()) {
// set the url and open the webpage
- CMStringA szUrl("https://www.visualcrossing.com/weather-history/" + mir_urlEncode(T2Utf(id)) + "/metric");
+ CMStringA szUrl("https://www.visualcrossing.com/weather-history/" + mir_urlEncode(T2Utf(wszID)) + "/metric");
Utils_OpenUrl(szUrl);
}
diff --git a/protocols/Weather/src/weather_conv.cpp b/protocols/Weather/src/weather_conv.cpp
index d9f35264b0..4d2c79f0ce 100644
--- a/protocols/Weather/src/weather_conv.cpp
+++ b/protocols/Weather/src/weather_conv.cpp
@@ -399,7 +399,7 @@ char* GetSearchStr(char *dis)
// dis = the string to parse
// return value = the parsed string
-wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t *str)
+CMStringW GetDisplay(WEATHERINFO *w, const wchar_t *dis)
{
wchar_t lpzDate[32], chr;
char name[256], temp[2];
@@ -407,7 +407,7 @@ wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t *str)
size_t i;
// Clear the string
- str[0] = 0;
+ CMStringW str;
// looking character by character
for (i = 0; i < mir_wstrlen(dis); i++) {
@@ -416,10 +416,10 @@ wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t *str)
i++;
chr = dis[i];
switch (chr) {
- case '%': mir_wstrcat(str, L"%"); break;
- case 't': mir_wstrcat(str, L"\t"); break;
- case 'n': mir_wstrcat(str, L"\r\n"); break;
- case '\\': mir_wstrcat(str, L"\\"); break;
+ case '%': str.Append(L"%"); break;
+ case 't': str.Append(L"\t"); break;
+ case 'n': str.Append(L"\r\n"); break;
+ case '\\': str.Append(L"\\"); break;
}
}
@@ -430,29 +430,31 @@ wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t *str)
// turn capitalized characters to small case
if (chr < 'a' && chr != '[' && chr != '%') chr = (char)((int)chr + 32);
switch (chr) {
- case 'c': mir_wstrcat(str, w->cond); break;
+ case 'c': str.Append(w->cond); break;
case 'd': // get the current date
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, nullptr, nullptr, lpzDate, _countof(lpzDate));
- mir_wstrcat(str, lpzDate); break;
- case 'e': mir_wstrcat(str, w->dewpoint); break;
- case 'f': mir_wstrcat(str, w->feel); break;
- case 'h': mir_wstrcat(str, w->high); break;
- case 'i': mir_wstrcat(str, w->winddir); break;
- case 'l': mir_wstrcat(str, w->low); break;
- case 'm': mir_wstrcat(str, w->humid); break;
- case 'n': mir_wstrcat(str, w->city); break;
- case 'p': mir_wstrcat(str, w->pressure); break;
- case 'r': mir_wstrcat(str, w->sunrise); break;
- case 's': mir_wstrcat(str, w->id); break;
- case 't': mir_wstrcat(str, w->temp); break;
+ str.Append(lpzDate); break;
+ case 'e': str.Append(w->dewpoint); break;
+ case 'f': str.Append(w->feel); break;
+ case 'h': str.Append(w->high); break;
+ case 'i': str.Append(w->winddir); break;
+ case 'l': str.Append(w->low); break;
+ case 'm': str.Append(w->humid); break;
+ case 'n': str.Append(w->city); break;
+ case 'p': str.Append(w->pressure); break;
+ case 'r': str.Append(w->sunrise); break;
+ case 's': str.Append(w->id); break;
+ case 't': str.Append(w->temp); break;
case 'u':
- if (mir_wstrcmp(w->update, NODATA)) mir_wstrcat(str, w->update);
- else mir_wstrcat(str, TranslateT("<unknown time>"));
+ if (mir_wstrcmp(w->update, NODATA))
+ str.Append(w->update);
+ else
+ str.Append(TranslateT("<unknown time>"));
break;
- case 'v': mir_wstrcat(str, w->vis); break;
- case 'w': mir_wstrcat(str, w->wind); break;
- case 'y': mir_wstrcat(str, w->sunset); break;
- case '%': mir_wstrcat(str, L"%"); break;
+ case 'v': str.Append(w->vis); break;
+ case 'w': str.Append(w->wind); break;
+ case 'y': str.Append(w->sunset); break;
+ case '%': str.Append(L"%"); break;
case '[': // custom variables
i++;
name[0] = 0;
@@ -464,17 +466,14 @@ wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t *str)
// access the database to get its value
if (!db_get_ws(w->hContact, WEATHERCONDITION, name, &dbv)) {
if (dbv.pwszVal != TranslateW(NODATA) && dbv.pwszVal != TranslateT("<Error>"))
- mir_wstrcat(str, dbv.pwszVal);
+ str.Append(dbv.pwszVal);
db_free(&dbv);
}
break;
}
}
// if the character is not a variable, write the original character to the new string
- else {
- mir_snwprintf(lpzDate, L"%c", dis[i]);
- mir_wstrcat(str, lpzDate);
- }
+ else str.AppendChar(dis[i]);
}
return str;
diff --git a/protocols/Weather/src/weather_data.cpp b/protocols/Weather/src/weather_data.cpp
index a02ae0dc1f..76e50ca139 100644
--- a/protocols/Weather/src/weather_data.cpp
+++ b/protocols/Weather/src/weather_data.cpp
@@ -26,18 +26,6 @@ saving individual weather data for a weather contact.
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////////////////
-// get station ID from DB
-// hContact = the current contact handle
-// return value = the string for station ID
-
-void CWeatherProto::GetStationID(MCONTACT hContact, wchar_t *id, int idlen)
-{
- // accessing the database
- if (db_get_wstatic(hContact, m_szModuleName, "ID", id, idlen))
- id[0] = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
// initialize weather info by loading values from database
// hContact = current contact handle
// return value = the current weather information in WEATHERINFO struct
@@ -49,7 +37,7 @@ WEATHERINFO CWeatherProto::LoadWeatherInfo(MCONTACT hContact)
// if the string is not found in database, a value of "N/A" is stored in the field
WEATHERINFO winfo;
winfo.hContact = hContact;
- GetStationID(hContact, winfo.id, _countof(winfo.id));
+ wcsncpy_s(winfo.id, getMStringW(hContact, "ID"), _countof(winfo.id));
if (db_get_wstatic(hContact, m_szModuleName, "Nick", winfo.city, _countof(winfo.city)))
wcsncpy(winfo.city, NODATA, _countof(winfo.city) - 1);
@@ -121,7 +109,7 @@ void CWeatherProto::EraseAllInfo()
// if no default station find, assign a new one
if (opt.Default[0] == 0) {
- GetStationID(hContact, opt.Default, _countof(opt.Default));
+ wcsncpy_s(opt.Default, getMStringW(hContact, "ID"), _countof(opt.Default));
opt.DefStn = hContact;
if (!getWString(hContact, "Nick", &dbv)) {
diff --git a/protocols/Weather/src/weather_mwin.cpp b/protocols/Weather/src/weather_mwin.cpp
index 57b775c81b..058c6e56ad 100644
--- a/protocols/Weather/src/weather_mwin.cpp
+++ b/protocols/Weather/src/weather_mwin.cpp
@@ -340,7 +340,7 @@ void CWeatherProto::InitMwin(void)
g_plugin.addFont(&fontid);
for (auto &hContact : AccContacts())
- if (g_plugin.getDword(hContact, "mwin"))
+ if (getDword(hContact, "mwin"))
AddFrameWindow(hContact);
hFontHook = HookEvent(ME_FONT_RELOAD, RedrawFrame);
@@ -349,7 +349,7 @@ void CWeatherProto::InitMwin(void)
void CWeatherProto::DestroyMwin(void)
{
for (auto &hContact : AccContacts()) {
- uint32_t frameId = g_plugin.getDword(hContact, "mwin");
+ uint32_t frameId = getDword(hContact, "mwin");
if (frameId)
CallService(MS_CLIST_FRAMES_REMOVEFRAME, frameId, 0);
}
diff --git a/protocols/Weather/src/weather_opt.cpp b/protocols/Weather/src/weather_opt.cpp
index a0b42e21be..765309e414 100644
--- a/protocols/Weather/src/weather_opt.cpp
+++ b/protocols/Weather/src/weather_opt.cpp
@@ -295,8 +295,6 @@ public:
/////////////////////////////////////////////////////////////////////////////////////////
// text option dialog
-#define VAR_LIST_OPT TranslateT("%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temp\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set\n----------\n\\n\tnew line")
-
struct
{
wchar_t c;
@@ -315,13 +313,40 @@ static controls[] =
{ 'S', IDC_BTITLE2, "StatusText" },
};
+struct
+{
+ wchar_t symbol;
+ const wchar_t *pwszText;
+}
+static variables[] =
+{
+ { 'c', LPGENW("Current condition") },
+ { 'd', LPGENW("Current date") },
+ { 'e', LPGENW("Dewpoint") },
+ { 'f', LPGENW("Feel-like temp") },
+ { 'h', LPGENW("Today's high") },
+ { 'i', LPGENW("Wind direction") },
+ { 'l', LPGENW("Today's low") },
+ { 'm', LPGENW("Humidity") },
+ { 'n', LPGENW("Station name") },
+ { 'p', LPGENW("Pressure") },
+ { 'r', LPGENW("Sunrise") },
+ { 's', LPGENW("Station ID") },
+ { 't', LPGENW("Temperature") },
+ { 'u', LPGENW("Update time") },
+ { 'v', LPGENW("Visibility") },
+ { 'w', LPGENW("Wind speed") },
+ { 'y', LPGENW("Sunset") },
+};
+
class COptionsTextDlg : public CWeatherDlgBase
{
- CCtrlMButton btnReset, tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8;
+ CCtrlMButton btnMore, btnReset, tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8;
public:
COptionsTextDlg(CWeatherProto *ppro) :
CWeatherDlgBase(ppro, IDD_TEXTOPT),
+ btnMore(this, IDC_MORE),
btnReset(this, IDC_RESET),
tm1(this, IDC_TM1),
tm2(this, IDC_TM2),
@@ -332,6 +357,7 @@ public:
tm7(this, IDC_TM7),
tm8(this, IDC_TM8)
{
+ btnMore.OnClick = Callback(this, &COptionsTextDlg::onClick_More);
btnReset.OnClick = Callback(this, &COptionsTextDlg::onClick_Reset);
tm1.OnClick = tm2.OnClick = tm3.OnClick = tm4.OnClick = tm5.OnClick = tm6.OnClick = tm7.OnClick = tm8.OnClick =
@@ -346,7 +372,12 @@ public:
SetWindowPos(m_hwnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
// generate the display text for variable list
- SetDlgItemTextW(m_hwnd, IDC_VARLIST, VAR_LIST_OPT);
+ CMStringW str;
+ for (auto &it : variables)
+ str.AppendFormat(L"%%%c\t%s\r\n", it.symbol, TranslateW(it.pwszText));
+ str.Append(L"----------\r\n");
+ str.AppendFormat(L"\\n\t%s\r\n", TranslateT("new line"));
+ SetDlgItemTextW(m_hwnd, IDC_VARLIST, str);
for (auto &it : controls)
SetDlgItemTextW(m_hwnd, it.id, m_proto->GetTextValue(it.c));
@@ -360,6 +391,7 @@ public:
tm6.MakeFlat();
tm7.MakeFlat();
tm8.MakeFlat();
+ btnMore.MakeFlat();
btnReset.MakeFlat();
return true;
}
@@ -367,8 +399,8 @@ public:
bool OnApply() override
{
// save the option
- wchar_t textstr[MAX_TEXT_SIZE];
for (auto &it : controls) {
+ wchar_t textstr[MAX_TEXT_SIZE];
GetDlgItemText(m_hwnd, it.id, textstr, _countof(textstr));
if (!mir_wstrcmpi(textstr, GetDefaultText(it.c)))
m_proto->delSetting(it.setting);
@@ -381,45 +413,55 @@ public:
return true;
}
+ void onClick_More(CCtrlButton *)
+ {
+ // heading
+ CMStringW str(TranslateT("Here is a list of custom variables that are currently available"));
+ str += L"\n\n";
+ m_proto->GetVarsDescr(str);
+
+ // display the list in a message box
+ MessageBox(nullptr, str, TranslateT("More Variables"), MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
+ }
+
void onClick_TM(CCtrlButton *pButton)
{
// display the menu
- RECT pos;
- GetWindowRect(pButton->GetHwnd(), &pos);
HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_TMMENU));
HMENU hMenu1 = GetSubMenu(hMenu, 0);
TranslateMenu(hMenu1);
- {
- auto &var = controls[pButton->GetCtrlId() - IDC_TM1];
-
- switch (TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, m_hwnd, nullptr)) {
- case ID_MPREVIEW:
- {
- // show the preview in a message box, using the weather data from the default station
- WEATHERINFO winfo = m_proto->LoadWeatherInfo(m_proto->opt.DefStn);
- wchar_t buf[MAX_TEXT_SIZE], str[4096];
- GetDlgItemTextW(m_hwnd, var.id, buf, _countof(buf));
- GetDisplay(&winfo, buf, str);
- MessageBox(nullptr, str, TranslateT("Weather Protocol Text Preview"), MB_OK | MB_TOPMOST);
- }
- break;
-
- case ID_MRESET:
- SetDlgItemTextW(m_hwnd, var.id, GetDefaultText(var.c));
- break;
+
+ auto &var = controls[pButton->GetCtrlId() - IDC_TM1];
+
+ RECT pos;
+ GetWindowRect(pButton->GetHwnd(), &pos);
+ switch (TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, m_hwnd, nullptr)) {
+ case ID_MPREVIEW:
+ {
+ // show the preview in a message box, using the weather data from the default station
+ WEATHERINFO winfo = m_proto->LoadWeatherInfo(m_proto->opt.DefStn);
+ wchar_t buf[MAX_TEXT_SIZE];
+ GetDlgItemTextW(m_hwnd, var.id, buf, _countof(buf));
+ MessageBox(nullptr, GetDisplay(&winfo, buf), TranslateT("Weather Protocol Text Preview"), MB_OK | MB_TOPMOST);
}
- DestroyMenu(hMenu);
+ break;
+
+ case ID_MRESET:
+ SetDlgItemTextW(m_hwnd, var.id, GetDefaultText(var.c));
+ break;
}
+ DestroyMenu(hMenu);
}
void onClick_Reset(CCtrlButton *)
{
// left click action selection menu
- RECT pos;
- GetWindowRect(btnReset.GetHwnd(), &pos);
HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_TMENU));
HMENU hMenu1 = GetSubMenu(hMenu, 0);
TranslateMenu(hMenu1);
+
+ RECT pos;
+ GetWindowRect(btnReset.GetHwnd(), &pos);
switch (TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, m_hwnd, nullptr)) {
case ID_T1:
// reset to the strings in memory, discard all changes
diff --git a/protocols/Weather/src/weather_popup.cpp b/protocols/Weather/src/weather_popup.cpp
index 91dcf51cb9..b9f938e41d 100644
--- a/protocols/Weather/src/weather_popup.cpp
+++ b/protocols/Weather/src/weather_popup.cpp
@@ -120,8 +120,8 @@ int CWeatherProto::WeatherPopup(MCONTACT hContact, bool bAlways)
POPUPDATAW ppd;
ppd.lchContact = hContact;
ppd.PluginData = ppd.lchIcon = GetStatusIcon(winfo.hContact);
- GetDisplay(&winfo, GetTextValue('P'), ppd.lpwzContactName);
- GetDisplay(&winfo, GetTextValue('p'), ppd.lpwzText);
+ wcsncpy_s(ppd.lpwzContactName, GetDisplay(&winfo, GetTextValue('P')), _TRUNCATE);
+ wcsncpy_s(ppd.lpwzText, GetDisplay(&winfo, GetTextValue('p')), _TRUNCATE);
ppd.PluginWindowProc = PopupDlgProc;
ppd.colorBack = (opt.UseWinColors) ? GetSysColor(COLOR_BTNFACE) : opt.BGColour;
ppd.colorText = (opt.UseWinColors) ? GetSysColor(COLOR_WINDOWTEXT) : opt.TextColour;
diff --git a/protocols/Weather/src/weather_proto.cpp b/protocols/Weather/src/weather_proto.cpp
index 6df768e7ac..3c5f8f1057 100644
--- a/protocols/Weather/src/weather_proto.cpp
+++ b/protocols/Weather/src/weather_proto.cpp
@@ -32,8 +32,6 @@ CWeatherProto::CWeatherProto(const char *protoName, const wchar_t *userName) :
HookProtoEvent(ME_CLIST_DOUBLECLICKED, &CWeatherProto::BriefInfoEvt);
HookProtoEvent(ME_CLIST_PREBUILDCONTACTMENU, &CWeatherProto::BuildContactMenu);
- InitMwin();
-
// load options and set defaults
LoadOptions();
@@ -63,6 +61,8 @@ CWeatherProto::~CWeatherProto()
void CWeatherProto::OnModulesLoaded()
{
+ InitMwin();
+
// timer for the first update
m_impl.m_start.Start(5000); // first update is 5 sec after load
diff --git a/protocols/Weather/src/weather_update.cpp b/protocols/Weather/src/weather_update.cpp
index 15713e110e..82437dbb72 100644
--- a/protocols/Weather/src/weather_update.cpp
+++ b/protocols/Weather/src/weather_update.cpp
@@ -32,7 +32,6 @@ menu items).
int CWeatherProto::UpdateWeather(MCONTACT hContact)
{
- wchar_t str2[MAX_TEXT_SIZE];
DBVARIANT dbv;
BOOL Ch = FALSE;
@@ -128,22 +127,18 @@ int CWeatherProto::UpdateWeather(MCONTACT hContact)
setWord(hContact, "Status", iStatus);
AvatarDownloaded(hContact);
- GetDisplay(&winfo, GetTextValue('C'), str2);
- db_set_ws(hContact, "CList", "MyHandle", str2);
+ db_set_ws(hContact, "CList", "MyHandle", GetDisplay(&winfo, GetTextValue('C')));
- GetDisplay(&winfo, GetTextValue('S'), str2);
- if (str2[0])
+ CMStringW str2(GetDisplay(&winfo, GetTextValue('S')));
+ if (!str2.IsEmpty())
db_set_ws(hContact, "CList", "StatusMsg", str2);
else
db_unset(hContact, "CList", "StatusMsg");
-
- ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, nullptr, (LPARAM)(str2[0] ? str2 : nullptr));
+ ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, nullptr, (LPARAM)(str2.IsEmpty() ? nullptr : str2.c_str()));
// save descriptions in MyNotes
- GetDisplay(&winfo, GetTextValue('N'), str2);
- db_set_ws(hContact, "UserInfo", "MyNotes", str2);
- GetDisplay(&winfo, GetTextValue('X'), str2);
- db_set_ws(hContact, WEATHERCONDITION, "WeatherInfo", str2);
+ db_set_ws(hContact, "UserInfo", "MyNotes", GetDisplay(&winfo, GetTextValue('N')));
+ db_set_ws(hContact, WEATHERCONDITION, "WeatherInfo", GetDisplay(&winfo, GetTextValue('X')));
// set the update tag
setByte(hContact, "IsUpdated", TRUE);
@@ -174,8 +169,7 @@ int CWeatherProto::UpdateWeather(MCONTACT hContact)
db_free(&dbv);
if (file != nullptr) {
// write data to the file and close
- GetDisplay(&winfo, GetTextValue('E'), str2);
- fputws(str2, file);
+ fputws(GetDisplay(&winfo, GetTextValue('E')), file);
fclose(file);
}
}
@@ -183,9 +177,7 @@ int CWeatherProto::UpdateWeather(MCONTACT hContact)
if (getByte(hContact, "History")) {
// internal log using history
- GetDisplay(&winfo, GetTextValue('H'), str2);
-
- T2Utf szMessage(str2);
+ T2Utf szMessage(GetDisplay(&winfo, GetTextValue('H')));
DBEVENTINFO dbei = {};
dbei.szModule = m_szModuleName;
@@ -381,6 +373,7 @@ static double g_elevation = 0;
static void getData(OBJLIST<WIDATAITEM> &arValues, const JSONNode &node)
{
+ arValues.insert(new WIDATAITEM(LPGENW("Date"), L"", parseConditions(node["datetime"].as_mstring())));
arValues.insert(new WIDATAITEM(LPGENW("Condition"), L"", parseConditions(node["conditions"].as_mstring())));
arValues.insert(new WIDATAITEM(LPGENW("Temperature"), L"C", node["temp"].as_mstring()));
arValues.insert(new WIDATAITEM(LPGENW("High"), L"C", node["tempmax"].as_mstring()));
@@ -403,15 +396,14 @@ static void getData(OBJLIST<WIDATAITEM> &arValues, const JSONNode &node)
int CWeatherProto::GetWeatherData(MCONTACT hContact)
{
// get each part of the id's
- wchar_t id[256];
- GetStationID(hContact, id, _countof(id));
- if (id[0] == 0)
+ CMStringW wszID(getMStringW(hContact, "ID"));
+ if (wszID.IsEmpty())
return INVALID_ID;
uint16_t cond = NA;
// download the html file from the internet
- WeatherReply reply(RunQuery(id, 7));
+ WeatherReply reply(RunQuery(wszID, 7));
if (!reply)
return reply.error();
@@ -478,6 +470,34 @@ int CWeatherProto::GetWeatherData(MCONTACT hContact)
}
/////////////////////////////////////////////////////////////////////////////////////////
+
+static int enumSettings(const char *pszSetting, void *param)
+{
+ auto *pList = (OBJLIST<char>*)param;
+ if (!pList->find((char*)pszSetting))
+ pList->insert(newStr(pszSetting));
+ return 0;
+}
+
+void CWeatherProto::GetVarsDescr(CMStringW &wszDescr)
+{
+ OBJLIST<char> vars(10, strcmp);
+ for (int i = 1; i <= 7; i++)
+ vars.insert(newStr(CMStringA(FORMAT, "Forecast Day %d", i)));
+
+ for (auto &cc : AccContacts())
+ db_enum_settings(cc, &enumSettings, WEATHERCONDITION, &vars);
+
+ CMStringW str;
+ for (auto &it : vars) {
+ if (!str.IsEmpty())
+ str.Append(L", ");
+ str.AppendFormat(L"%%[%S]", it);
+ }
+ wszDescr += str;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
// main auto-update timer
void CWeatherProto::DoUpdate()
diff --git a/protocols/Weather/src/weather_userinfo.cpp b/protocols/Weather/src/weather_userinfo.cpp
index 0679ddf6e9..1207d01b4a 100644
--- a/protocols/Weather/src/weather_userinfo.cpp
+++ b/protocols/Weather/src/weather_userinfo.cpp
@@ -151,8 +151,6 @@ public:
{
m_list.DeleteAllItems();
- wchar_t str[4096];
-
// load weather information from the contact into the WEATHERINFO struct
WEATHERINFO winfo = m_proto->LoadWeatherInfo(hContact);
// check if data exist. If not, display error message box
@@ -160,13 +158,11 @@ public:
SetDlgItemTextW(m_hwnd, IDC_MTEXT, TranslateT("No information available.\r\nPlease update weather condition first."));
else {
// set the display text and show the message box
- GetDisplay(&winfo, m_proto->GetTextValue('B'), str);
- SetDlgItemTextW(m_hwnd, IDC_MTEXT, str);
+ SetDlgItemTextW(m_hwnd, IDC_MTEXT, GetDisplay(&winfo, m_proto->GetTextValue('B')));
}
- GetDisplay(&winfo, L"%c, %t", str);
SetWindowTextW(m_hwnd, winfo.city);
- SetDlgItemTextW(m_hwnd, IDC_HEADERBAR, str);
+ SetDlgItemTextW(m_hwnd, IDC_HEADERBAR, GetDisplay(&winfo, L"%c, %t"));
// get all the settings and store them in a temporary list
LIST<char> arSettings(10);
@@ -311,9 +307,8 @@ public:
ppro = (CWeatherProto *)Proto_GetContactInstance(m_hContact);
// load weather info for the contact
- wchar_t str[MAX_TEXT_SIZE];
WEATHERINFO w = ppro->LoadWeatherInfo(m_hContact);
- SetDlgItemText(m_hwnd, IDC_INFO1, GetDisplay(&w, TranslateT("Current condition for %n"), str));
+ SetDlgItemText(m_hwnd, IDC_INFO1, GetDisplay(&w, TranslateT("Current condition for %n")));
SendDlgItemMessage(m_hwnd, IDC_INFOICON, STM_SETICON, (WPARAM)ppro->GetStatusIconBig(m_hContact), 0);
@@ -327,19 +322,16 @@ public:
SendDlgItemMessage(m_hwnd, IDC_INFO2, WM_SETFONT, (WPARAM)CreateFontIndirect(&lf), 0);
// set the text for displaying other current weather conditions data
- GetDisplay(&w, L"%c %t", str);
- SetDlgItemText(m_hwnd, IDC_INFO2, str);
+ SetDlgItemText(m_hwnd, IDC_INFO2, GetDisplay(&w, L"%c %t"));
SetDlgItemText(m_hwnd, IDC_INFO3, w.feel);
SetDlgItemText(m_hwnd, IDC_INFO4, w.pressure);
- GetDisplay(&w, L"%i %w", str);
- SetDlgItemText(m_hwnd, IDC_INFO5, str);
+ SetDlgItemText(m_hwnd, IDC_INFO5, GetDisplay(&w, L"%i %w"));
SetDlgItemText(m_hwnd, IDC_INFO6, w.dewpoint);
SetDlgItemText(m_hwnd, IDC_INFO7, w.sunrise);
SetDlgItemText(m_hwnd, IDC_INFO8, w.sunset);
SetDlgItemText(m_hwnd, IDC_INFO9, w.high);
SetDlgItemText(m_hwnd, IDC_INFO10, w.low);
- GetDisplay(&w, TranslateT("Last update on: %u"), str);
- SetDlgItemText(m_hwnd, IDC_INFO11, str);
+ SetDlgItemText(m_hwnd, IDC_INFO11, GetDisplay(&w, TranslateT("Last update on: %u")));
SetDlgItemText(m_hwnd, IDC_INFO12, w.humid);
SetDlgItemText(m_hwnd, IDC_INFO13, w.vis);
return true;
diff --git a/src/mir_app/src/MHttpRequest.cpp b/src/mir_app/src/MHttpRequest.cpp
index b44da4c750..a95678e5e7 100644
--- a/src/mir_app/src/MHttpRequest.cpp
+++ b/src/mir_app/src/MHttpRequest.cpp
@@ -63,6 +63,15 @@ MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM &par
return pReq;
}
+MIR_APP_DLL(MHttpRequest *) operator<<(MHttpRequest *pReq, const BOOL_PARAM &param)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%s", param.szName, param.bValue ? "true" : "false");
+ return pReq;
+}
+
MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM &param)
{
CMStringA &s = pReq->m_szParam;
diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def
index 253c54a094..db67abbdcf 100644
--- a/src/mir_app/src/mir_app.def
+++ b/src/mir_app/src/mir_app.def
@@ -1003,3 +1003,4 @@ Proto_CanDeleteHistory @1118 NONAME
?data@JsonReply@@QBEAAVJSONNode@@XZ @1134 NONAME
?error@JsonReply@@QBEHXZ @1135 NONAME
??BJsonReply@@QBE_NXZ @1136 NONAME
+??6@YGPAUMHttpRequest@@PAU0@ABUBOOL_PARAM@@@Z @1137 NONAME
diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def
index 61117b7741..4b314b0a54 100644
--- a/src/mir_app/src/mir_app64.def
+++ b/src/mir_app/src/mir_app64.def
@@ -1003,3 +1003,4 @@ Proto_CanDeleteHistory @1118 NONAME
?data@JsonReply@@QEBAAEAVJSONNode@@XZ @1134 NONAME
?error@JsonReply@@QEBAHXZ @1135 NONAME
??BJsonReply@@QEBA_NXZ @1136 NONAME
+??6@YAPEAUMHttpRequest@@PEAU0@AEBUBOOL_PARAM@@@Z @1137 NONAME
diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp
index 185e320636..196b22a816 100644
--- a/src/mir_app/src/netlib_websocket.cpp
+++ b/src/mir_app/src/netlib_websocket.cpp
@@ -187,6 +187,8 @@ static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, int64_t dataLen,
void MWebSocket::sendText(const char *pData)
{
if (m_hConn && pData) {
+ Netlib_Dump(m_hConn, pData, strlen(pData), true, 0);
+
mir_cslock lck(m_cs);
WebSocket_Send(m_hConn, pData, strlen(pData), 1);
}
diff --git a/src/mir_app/src/proto_order.cpp b/src/mir_app/src/proto_order.cpp
index d8694f0197..c46733feac 100644
--- a/src/mir_app/src/proto_order.cpp
+++ b/src/mir_app/src/proto_order.cpp
@@ -25,12 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "stdafx.h"
#include "clc.h"
-struct ProtocolData
-{
- char *RealName;
- int enabled;
-};
-
bool CheckProtocolOrder(void)
{
bool changed = false;
@@ -127,15 +121,13 @@ class CProtocolOrderOpts : public CDlgBase
if (!ProtoToInclude(pa))
continue;
- ProtocolData *PD = (ProtocolData*)mir_alloc(sizeof(ProtocolData));
- PD->RealName = pa->szModuleName;
- PD->enabled = pa->IsEnabled() && isProtoSuitable(pa->ppro);
-
- tvis.item.lParam = (LPARAM)PD;
- tvis.item.pszText = pa->tszAccountName;
- tvis.item.stateMask = TVIS_STATEIMAGEMASK;
- tvis.item.state = INDEXTOSTATEIMAGEMASK((PD->enabled) ? (pa->bIsVisible ? 2 : 1) : 3);
- m_order.InsertItem(&tvis);
+ if (pa->IsEnabled() && isProtoSuitable(pa->ppro)) {
+ tvis.item.lParam = (LPARAM)pa->szModuleName;
+ tvis.item.pszText = pa->tszAccountName;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(pa->bIsVisible ? 2 : 1);
+ m_order.InsertItem(&tvis);
+ }
}
}
@@ -151,7 +143,6 @@ public:
m_btnReset.OnClick = Callback(this, &CProtocolOrderOpts::onReset_Click);
m_order.SetFlags(MTREE_CHECKBOX | MTREE_DND);
- m_order.OnDeleteItem = Callback(this, &CProtocolOrderOpts::onOrder_DeleteItem);
}
bool OnInitDialog() override
@@ -176,12 +167,10 @@ public:
m_order.GetItem(&tvi);
if (tvi.lParam != 0) {
- ProtocolData *ppd = (ProtocolData*)tvi.lParam;
- PROTOACCOUNT *pa = Proto_GetAccount(ppd->RealName);
+ PROTOACCOUNT *pa = Proto_GetAccount((char *)tvi.lParam);
if (pa != nullptr) {
pa->iOrder = idx++;
- if (ppd->enabled)
- pa->bIsVisible = m_order.GetCheckState(tvi.hItem) != 0;
+ pa->bIsVisible = m_order.GetCheckState(tvi.hItem) != 0;
}
}
@@ -209,13 +198,6 @@ public:
FillTree();
NotifyChange();
}
-
- void onOrder_DeleteItem(CCtrlTreeView::TEventInfo *env)
- {
- NMTREEVIEW *pnmtv = env->nmtv;
- if (pnmtv)
- mir_free((ProtocolData*)pnmtv->itemOld.lParam);
- }
};
int ProtocolOrderOptInit(WPARAM wParam, LPARAM)
diff --git a/src/mir_app/src/srmm_base.cpp b/src/mir_app/src/srmm_base.cpp
index fa4f27ce57..bca6e49148 100644
--- a/src/mir_app/src/srmm_base.cpp
+++ b/src/mir_app/src/srmm_base.cpp
@@ -1077,8 +1077,8 @@ bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact)
bool CSrmmBaseDialog::PasteFilesAsURL(HDROP hDrop)
{
- bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
- if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !isShift) // hidden setting: Chat/ShiftDropFilePasteURL
+ bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0, isCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
+ if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !(isShift && !isCtrl)) // hidden setting: Chat/ShiftDropFilePasteURL
return false;
int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0);
diff --git a/tools/build_scripts/z2_PackPluginUpdater.bat b/tools/build_scripts/z2_PackPluginUpdater.bat
index 1d142092a1..a5f15f5ae8 100644
--- a/tools/build_scripts/z2_PackPluginUpdater.bat
+++ b/tools/build_scripts/z2_PackPluginUpdater.bat
@@ -54,6 +54,7 @@ for /f %%a in ('dir plugins\*.dll /B /L') do (
if /I "%%a"=="FTPFile.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Libs\libcurl.mir" "Libs\libssh2.mir")
if /I "%%a"=="GG.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Icons\Proto_GG.dll")
if /I "%%a"=="HistoryPP.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Icons\HistoryPP_icons.dll")
+ if /I "%%a"=="ICQCorp.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Icons\Proto_ICQ.dll")
if /I "%%a"=="IRC.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Icons\Proto_IRC.dll")
if /I "%%a"=="Import.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Plugins\Import\*.ini")
if /I "%%a"=="Jabber.dll" (%ZipIt% "%Arch%\Plugins\%%~na.zip" "Icons\Proto_Jabber.dll" "Icons\xStatus_Jabber.dll" "Libs\libsignal.mir")